clud-bug 0.7.0-rc.2 → 0.7.0-rc.21
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 +20 -0
- package/data/canonical-v1.json +37 -0
- package/dist/cli/configure-github.d.ts +58 -0
- package/dist/cli/configure-github.d.ts.map +1 -0
- package/dist/cli/configure-github.js +242 -0
- package/dist/cli/configure-github.js.map +1 -0
- package/dist/cli/hooks.d.ts +51 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +138 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +949 -35
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/post-check-run.d.ts +16 -0
- package/dist/cli/post-check-run.d.ts.map +1 -0
- package/dist/cli/post-check-run.js +93 -0
- package/dist/cli/post-check-run.js.map +1 -0
- package/dist/cli/review-prompt.d.ts +43 -0
- package/dist/cli/review-prompt.d.ts.map +1 -0
- package/dist/cli/review-prompt.js +287 -0
- package/dist/cli/review-prompt.js.map +1 -0
- package/dist/cli/skills.d.ts +7 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +42 -2
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/update-notifier.d.ts +17 -0
- package/dist/cli/update-notifier.d.ts.map +1 -0
- package/dist/cli/update-notifier.js +88 -0
- package/dist/cli/update-notifier.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +60 -11
- package/dist/cli/update.js.map +1 -1
- package/dist/core/auto-resolve.d.ts +235 -0
- package/dist/core/auto-resolve.d.ts.map +1 -0
- package/dist/core/auto-resolve.js +293 -0
- package/dist/core/auto-resolve.js.map +1 -0
- package/dist/core/budget-plan.d.ts +125 -0
- package/dist/core/budget-plan.d.ts.map +1 -0
- package/dist/core/budget-plan.js +211 -0
- package/dist/core/budget-plan.js.map +1 -0
- package/dist/core/check-verdict.d.ts +31 -0
- package/dist/core/check-verdict.d.ts.map +1 -0
- package/dist/core/check-verdict.js +66 -0
- package/dist/core/check-verdict.js.map +1 -0
- package/dist/core/configure-github.d.ts +179 -0
- package/dist/core/configure-github.d.ts.map +1 -0
- package/dist/core/configure-github.js +289 -0
- package/dist/core/configure-github.js.map +1 -0
- package/dist/core/design.d.ts +37 -0
- package/dist/core/design.d.ts.map +1 -0
- package/dist/core/design.js +47 -0
- package/dist/core/design.js.map +1 -0
- package/dist/core/diff-findings.d.ts +96 -0
- package/dist/core/diff-findings.d.ts.map +1 -0
- package/dist/core/diff-findings.js +261 -0
- package/dist/core/diff-findings.js.map +1 -0
- package/dist/core/formal-review.d.ts +79 -0
- package/dist/core/formal-review.d.ts.map +1 -0
- package/dist/core/formal-review.js +71 -0
- package/dist/core/formal-review.js.map +1 -0
- package/dist/core/index.d.ts +15 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +53 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/inline-threads.d.ts +160 -0
- package/dist/core/inline-threads.d.ts.map +1 -0
- package/dist/core/inline-threads.js +369 -0
- package/dist/core/inline-threads.js.map +1 -0
- package/dist/core/multi-pass-aggregate.d.ts +161 -0
- package/dist/core/multi-pass-aggregate.d.ts.map +1 -0
- package/dist/core/multi-pass-aggregate.js +457 -0
- package/dist/core/multi-pass-aggregate.js.map +1 -0
- package/dist/core/plan-review.d.ts +57 -0
- package/dist/core/plan-review.d.ts.map +1 -0
- package/dist/core/plan-review.js +77 -0
- package/dist/core/plan-review.js.map +1 -0
- package/dist/core/prompt-builder.d.ts +18 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +24 -2
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/render.d.ts.map +1 -1
- package/dist/core/render.js +1 -19
- package/dist/core/render.js.map +1 -1
- package/dist/core/resolve-verifier.d.ts +53 -0
- package/dist/core/resolve-verifier.d.ts.map +1 -0
- package/dist/core/resolve-verifier.js +204 -0
- package/dist/core/resolve-verifier.js.map +1 -0
- package/dist/core/review-context.d.ts +39 -0
- package/dist/core/review-context.d.ts.map +1 -0
- package/dist/core/review-context.js +99 -0
- package/dist/core/review-context.js.map +1 -0
- package/dist/core/review-plan.d.ts +219 -0
- package/dist/core/review-plan.d.ts.map +1 -0
- package/dist/core/review-plan.js +274 -0
- package/dist/core/review-plan.js.map +1 -0
- package/dist/core/review-writeback.d.ts +74 -0
- package/dist/core/review-writeback.d.ts.map +1 -1
- package/dist/core/review-writeback.js +71 -4
- package/dist/core/review-writeback.js.map +1 -1
- package/dist/core/skills.d.ts +25 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +100 -4
- package/dist/core/skills.js.map +1 -1
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +4 -0
- package/dist/core/version.js.map +1 -0
- package/package.json +11 -5
- package/src/cli/configure-github.ts +329 -0
- package/src/cli/hooks.ts +155 -0
- package/src/cli/index.ts +6 -0
- package/src/cli/main.ts +1031 -37
- package/src/cli/post-check-run.ts +115 -0
- package/src/cli/review-prompt.ts +355 -0
- package/src/cli/skills.ts +42 -2
- package/src/cli/update-notifier.ts +100 -0
- package/src/cli/update.ts +60 -11
- package/src/core/auto-resolve.ts +488 -0
- package/src/core/budget-plan.ts +324 -0
- package/src/core/check-verdict.ts +93 -0
- package/src/core/configure-github.ts +497 -0
- package/src/core/design.ts +73 -0
- package/src/core/diff-findings.ts +323 -0
- package/src/core/formal-review.ts +168 -0
- package/src/core/index.ts +188 -0
- package/src/core/inline-threads.ts +471 -0
- package/src/core/multi-pass-aggregate.ts +591 -0
- package/src/core/plan-review.ts +136 -0
- package/src/core/prompt-builder.ts +44 -2
- package/src/core/render.ts +10 -16
- package/src/core/resolve-verifier.ts +254 -0
- package/src/core/review-context.ts +106 -0
- package/src/core/review-plan.ts +485 -0
- package/src/core/review-writeback.ts +161 -4
- package/src/core/skills.ts +128 -5
- package/src/core/version.ts +3 -0
- package/templates/clud-bug-review.md.tmpl +115 -0
- package/templates/skills/design/design-system-consistency.md +41 -0
- package/templates/skills/design/frontend-a11y.md +41 -0
- package/templates/skills/design/visual-polish.md +41 -0
- package/templates/workflow-py.yml.tmpl +92 -2
- package/templates/workflow-ts.yml.tmpl +92 -2
- package/templates/workflow.yml.tmpl +173 -2
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
export type Severity = 'critical' | 'minor' | 'preexisting';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape this module needs from a review finding. Both the npm CLI
|
|
4
|
+
* and the App's `Finding` type satisfy this; the App also carries
|
|
5
|
+
* `reasoning` + nullable `file`/`line` which we tolerate via the optional
|
|
6
|
+
* fields below.
|
|
7
|
+
*/
|
|
8
|
+
export interface FindingForThread {
|
|
9
|
+
severity: Severity;
|
|
10
|
+
skill: string;
|
|
11
|
+
file?: string;
|
|
12
|
+
line?: number;
|
|
13
|
+
summary: string;
|
|
14
|
+
reasoning?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-file unified-diff entry. Matches the shape returned by `gh api
|
|
18
|
+
* repos/{owner}/{repo}/pulls/{n}/files` (after JSON.parse) AND
|
|
19
|
+
* `octokit.rest.pulls.listFiles(...).data` so the App can pass either
|
|
20
|
+
* directly.
|
|
21
|
+
*/
|
|
22
|
+
export interface DiffFile {
|
|
23
|
+
filename: string;
|
|
24
|
+
patch?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Hashes the canonical `${file}:${line}:${severity}:${skill}:${summary[:100]}`
|
|
28
|
+
* tuple to 16 hex characters. Stable across consumers — the App computes
|
|
29
|
+
* the same hash for the same finding, so threads posted by one consumer
|
|
30
|
+
* stay matchable by the other.
|
|
31
|
+
*
|
|
32
|
+
* Summary is truncated to 100 chars BEFORE hashing so small wording tweaks
|
|
33
|
+
* on subsequent review passes don't generate a fresh id and orphan the
|
|
34
|
+
* prior thread.
|
|
35
|
+
*
|
|
36
|
+
* Distinct from `findingIdentity` in `./diff-findings` (which returns the
|
|
37
|
+
* plain unhashed string — used by the Resolved/Still-open block matcher).
|
|
38
|
+
* Both helpers coexist on purpose: the unhashed string is human-readable
|
|
39
|
+
* for in-prose matching; the hash is short + collision-safe for embedding
|
|
40
|
+
* in `<!-- finding-id: ... -->` comment-body markers.
|
|
41
|
+
*/
|
|
42
|
+
export declare function findingId(finding: {
|
|
43
|
+
file?: string;
|
|
44
|
+
line?: number;
|
|
45
|
+
severity: Severity;
|
|
46
|
+
skill: string;
|
|
47
|
+
summary: string;
|
|
48
|
+
}): string;
|
|
49
|
+
/**
|
|
50
|
+
* Returns the set of 1-indexed HEAD-side line numbers a unified-diff patch
|
|
51
|
+
* touches (added + context lines). GitHub only accepts inline review
|
|
52
|
+
* comments on lines that appear in the diff, so this gates whether a
|
|
53
|
+
* finding's `line` can become a thread.
|
|
54
|
+
*
|
|
55
|
+
* "Touched" = appears on the RIGHT side of the unified diff: added (`+`)
|
|
56
|
+
* lines OR unchanged context (` `) lines inside any hunk. Removed (`-`)
|
|
57
|
+
* lines are LEFT-side only and can't anchor inline comments.
|
|
58
|
+
*
|
|
59
|
+
* If `patch` is undefined (binary file, GitHub truncation) returns an empty
|
|
60
|
+
* set — the caller falls back to the summary comment for those findings.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseHeadLines(patch: string | undefined): Set<number>;
|
|
63
|
+
/**
|
|
64
|
+
* Returns true when the finding's `(file, line)` can be anchored as an
|
|
65
|
+
* inline comment on the current diff. `diffFiles` is the per-file diff
|
|
66
|
+
* (as returned by `gh api repos/.../pulls/.../files`).
|
|
67
|
+
*/
|
|
68
|
+
export declare function findingAnchorable(finding: {
|
|
69
|
+
file?: string;
|
|
70
|
+
line?: number;
|
|
71
|
+
}, diffFiles: ReadonlyArray<DiffFile>): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Builds the `{codeBefore, codeAfter, diffAtAnchor?}` triple Wave 5b's
|
|
74
|
+
* auto-resolve verifier consumes for a single finding, pulled out of the
|
|
75
|
+
* unified-diff hunk that touches the finding's line.
|
|
76
|
+
*
|
|
77
|
+
* Works entirely from the in-hand patch — no fresh API calls. The patch
|
|
78
|
+
* already encodes BEFORE (lines with `-` or context) and AFTER (lines with
|
|
79
|
+
* `+` or context) for every hunk the PR touches.
|
|
80
|
+
*
|
|
81
|
+
* Returns empty strings for `codeBefore`/`codeAfter` when the finding's
|
|
82
|
+
* file isn't in the diff at all. The verifier sees "unchanged" and
|
|
83
|
+
* typically returns NOT_ADDRESSED / UNCERTAIN, which is the correct
|
|
84
|
+
* behavior — an unchanged file isn't a fix.
|
|
85
|
+
*/
|
|
86
|
+
export declare function extractAnchorContext(finding: {
|
|
87
|
+
file?: string;
|
|
88
|
+
line?: number;
|
|
89
|
+
}, diffFiles: ReadonlyArray<DiffFile>): {
|
|
90
|
+
codeBefore: string;
|
|
91
|
+
codeAfter: string;
|
|
92
|
+
diffAtAnchor?: string;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Body for a single inline-thread comment. Includes a hidden HTML marker
|
|
96
|
+
* carrying `findingId` so Wave 5b's auto-resolve can recover the canonical
|
|
97
|
+
* id from the thread's first comment body without persisting state.
|
|
98
|
+
*/
|
|
99
|
+
export declare function renderThreadBody(finding: FindingForThread): string;
|
|
100
|
+
/**
|
|
101
|
+
* Extracts the hashed `findingId` from a thread comment body that was
|
|
102
|
+
* rendered by `renderThreadBody`. Returns null when the marker is absent
|
|
103
|
+
* (a manual reply, or a comment posted by a different tool).
|
|
104
|
+
*
|
|
105
|
+
* Used by Wave 5b's auto-resolve to re-derive finding ids from existing
|
|
106
|
+
* GitHub threads on a stateless re-run.
|
|
107
|
+
*/
|
|
108
|
+
export declare function extractFindingIdFromBody(body: string): string | null;
|
|
109
|
+
/**
|
|
110
|
+
* Wave 5b — invert `renderThreadBody` to recover the original finding
|
|
111
|
+
* tuple from a bot-authored thread comment. The fix-push auto-resolve
|
|
112
|
+
* CLI verb uses this to reconstruct `PriorFinding` from the GraphQL
|
|
113
|
+
* `reviewThreads` response without persistent state.
|
|
114
|
+
*
|
|
115
|
+
* Returns null when the body doesn't match the canonical shape
|
|
116
|
+
* `<!-- finding-id: XX -->\n🔴|🟡 \`skill\`\n\nsummary[\n\nreasoning]`.
|
|
117
|
+
* Same start-anchored posture as `extractFindingIdFromBody` for anti-
|
|
118
|
+
* injection: a user reply that quotes the marker mid-body won't be
|
|
119
|
+
* misattributed as bot-authored.
|
|
120
|
+
*/
|
|
121
|
+
export declare function parseThreadBody(body: string): {
|
|
122
|
+
findingId: string;
|
|
123
|
+
severity: Severity;
|
|
124
|
+
skill: string;
|
|
125
|
+
summary: string;
|
|
126
|
+
reasoning?: string;
|
|
127
|
+
} | null;
|
|
128
|
+
/**
|
|
129
|
+
* One entry in the createReview `comments[]` array. Matches GitHub's REST
|
|
130
|
+
* shape exactly so callers can JSON.stringify directly into a `gh api` body.
|
|
131
|
+
*/
|
|
132
|
+
export interface InlineCommentPlan {
|
|
133
|
+
path: string;
|
|
134
|
+
line: number;
|
|
135
|
+
side: 'RIGHT';
|
|
136
|
+
body: string;
|
|
137
|
+
}
|
|
138
|
+
export interface PlanInlineThreadsResult {
|
|
139
|
+
/** Comment entries for the createReview `comments[]` field. */
|
|
140
|
+
comments: InlineCommentPlan[];
|
|
141
|
+
/** Findings the diff couldn't anchor (file or line not in any hunk). */
|
|
142
|
+
skipped: FindingForThread[];
|
|
143
|
+
/** Findings filtered out because severity is 'preexisting' (informational only). */
|
|
144
|
+
preexisting: FindingForThread[];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Partitions a flat findings list into the `comments[]` body for
|
|
148
|
+
* `POST /repos/:owner/:repo/pulls/:n/reviews` + the findings the caller
|
|
149
|
+
* should surface elsewhere (summary comment for `skipped`; never for
|
|
150
|
+
* `preexisting`).
|
|
151
|
+
*
|
|
152
|
+
* Pure function — no I/O. The CLI verb uses this to compute the API body;
|
|
153
|
+
* the App can use it after Wave 5c migration to share the same partitioning.
|
|
154
|
+
*/
|
|
155
|
+
export declare function planInlineThreads(findings: ReadonlyArray<FindingForThread>, diffFiles: ReadonlyArray<DiffFile>): PlanInlineThreadsResult;
|
|
156
|
+
export declare const REVIEW_THREADS_QUERY = "\n query ReviewThreads($owner: String!, $repo: String!, $pr: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pr) {\n reviewThreads(first: 100) {\n nodes {\n id\n isResolved\n comments(first: 100) {\n nodes {\n databaseId\n path\n line\n originalLine\n body\n author {\n login\n }\n }\n }\n }\n }\n }\n }\n }\n";
|
|
157
|
+
export declare const REVIEW_THREADS_STATE_QUERY = "\n query ReviewThreadStates($owner: String!, $repo: String!, $pr: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pr) {\n reviewThreads(first: 100) {\n nodes {\n id\n isResolved\n comments(first: 1) {\n nodes {\n databaseId\n body\n }\n }\n }\n }\n }\n }\n }\n";
|
|
158
|
+
export declare const RESOLVE_THREAD_MUTATION = "\n mutation ResolveThread($threadId: ID!) {\n resolveReviewThread(input: { threadId: $threadId }) {\n thread {\n isResolved\n }\n }\n }\n";
|
|
159
|
+
export declare const ADD_REPLY_MUTATION = "\n mutation AddThreadReply($threadId: ID!, $body: String!) {\n addPullRequestReviewThreadReply(\n input: { pullRequestReviewThreadId: $threadId, body: $body }\n ) {\n comment {\n id\n body\n }\n }\n }\n";
|
|
160
|
+
//# sourceMappingURL=inline-threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-threads.d.ts","sourceRoot":"","sources":["../../src/core/inline-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
// Wave 5a — D.2.X inline review threads (npm workflow parity, OSS port).
|
|
2
|
+
//
|
|
3
|
+
// This module holds the pure helpers + GraphQL operation strings that BOTH
|
|
4
|
+
// the npm workflow path and the hosted clud-bug-app use to attach a
|
|
5
|
+
// per-finding inline review thread to the PR (replacing the legacy
|
|
6
|
+
// "one big summary comment listing every finding" UX).
|
|
7
|
+
//
|
|
8
|
+
// The split between npm and app:
|
|
9
|
+
// - npm path (this package's CLI verb `post-inline-threads`) is stateless:
|
|
10
|
+
// it shells out to `gh api` for the REST createReview + `gh api graphql`
|
|
11
|
+
// for the GraphQL thread-id lookup, then EXITS. No persistent record —
|
|
12
|
+
// Wave 5b's auto-resolve re-queries GitHub on the next push and re-derives
|
|
13
|
+
// finding IDs from the comment-body markers we embed here.
|
|
14
|
+
// - app path keeps its Redis-backed persistence for performance (cross-
|
|
15
|
+
// push thread correlation without re-querying GraphQL every time). The
|
|
16
|
+
// pure helpers below are designed so a future Wave 5c swap can have the
|
|
17
|
+
// App import from this module + drop its local duplicates.
|
|
18
|
+
//
|
|
19
|
+
// Designed to share `findingId` semantics with clud-bug-app's
|
|
20
|
+
// `lib/inline-threads.ts:findingId` (SHA-256 of the canonical 5-tuple,
|
|
21
|
+
// truncated to 16 hex chars) so threads posted by one consumer stay
|
|
22
|
+
// matchable by the other after the App migrates.
|
|
23
|
+
import { createHash } from 'node:crypto';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Stable finding id — SHA-256 of the canonical 5-tuple, truncated to 16 hex.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Hashes the canonical `${file}:${line}:${severity}:${skill}:${summary[:100]}`
|
|
29
|
+
* tuple to 16 hex characters. Stable across consumers — the App computes
|
|
30
|
+
* the same hash for the same finding, so threads posted by one consumer
|
|
31
|
+
* stay matchable by the other.
|
|
32
|
+
*
|
|
33
|
+
* Summary is truncated to 100 chars BEFORE hashing so small wording tweaks
|
|
34
|
+
* on subsequent review passes don't generate a fresh id and orphan the
|
|
35
|
+
* prior thread.
|
|
36
|
+
*
|
|
37
|
+
* Distinct from `findingIdentity` in `./diff-findings` (which returns the
|
|
38
|
+
* plain unhashed string — used by the Resolved/Still-open block matcher).
|
|
39
|
+
* Both helpers coexist on purpose: the unhashed string is human-readable
|
|
40
|
+
* for in-prose matching; the hash is short + collision-safe for embedding
|
|
41
|
+
* in `<!-- finding-id: ... -->` comment-body markers.
|
|
42
|
+
*/
|
|
43
|
+
export function findingId(finding) {
|
|
44
|
+
const file = finding.file ?? '<no-file>';
|
|
45
|
+
const line = finding.line ?? 0;
|
|
46
|
+
const summary = finding.summary.slice(0, 100);
|
|
47
|
+
const payload = `${file}:${line}:${finding.severity}:${finding.skill}:${summary}`;
|
|
48
|
+
return createHash('sha256').update(payload).digest('hex').slice(0, 16);
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Diff anchor detection
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Returns the set of 1-indexed HEAD-side line numbers a unified-diff patch
|
|
55
|
+
* touches (added + context lines). GitHub only accepts inline review
|
|
56
|
+
* comments on lines that appear in the diff, so this gates whether a
|
|
57
|
+
* finding's `line` can become a thread.
|
|
58
|
+
*
|
|
59
|
+
* "Touched" = appears on the RIGHT side of the unified diff: added (`+`)
|
|
60
|
+
* lines OR unchanged context (` `) lines inside any hunk. Removed (`-`)
|
|
61
|
+
* lines are LEFT-side only and can't anchor inline comments.
|
|
62
|
+
*
|
|
63
|
+
* If `patch` is undefined (binary file, GitHub truncation) returns an empty
|
|
64
|
+
* set — the caller falls back to the summary comment for those findings.
|
|
65
|
+
*/
|
|
66
|
+
export function parseHeadLines(patch) {
|
|
67
|
+
const lines = new Set();
|
|
68
|
+
if (!patch)
|
|
69
|
+
return lines;
|
|
70
|
+
// Hunk header: @@ -oldStart,oldLines +newStart,newLines @@
|
|
71
|
+
// newLines defaults to 1 when omitted.
|
|
72
|
+
const hunkHeaderRe = /^@@\s+-\d+(?:,\d+)?\s+\+(\d+)(?:,(\d+))?\s+@@/;
|
|
73
|
+
let newLine = 0;
|
|
74
|
+
let inHunk = false;
|
|
75
|
+
for (const rawLine of patch.split('\n')) {
|
|
76
|
+
const m = rawLine.match(hunkHeaderRe);
|
|
77
|
+
if (m) {
|
|
78
|
+
newLine = Number.parseInt(m[1], 10);
|
|
79
|
+
inHunk = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!inHunk)
|
|
83
|
+
continue;
|
|
84
|
+
if (rawLine.startsWith('+') && !rawLine.startsWith('+++')) {
|
|
85
|
+
lines.add(newLine);
|
|
86
|
+
newLine++;
|
|
87
|
+
}
|
|
88
|
+
else if (rawLine.startsWith('-') && !rawLine.startsWith('---')) {
|
|
89
|
+
// LEFT-side only — does not advance the newLine counter.
|
|
90
|
+
}
|
|
91
|
+
else if (rawLine.startsWith('\\')) {
|
|
92
|
+
// "" — doesn't advance.
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Context line — counts as a HEAD-side line.
|
|
96
|
+
lines.add(newLine);
|
|
97
|
+
newLine++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns true when the finding's `(file, line)` can be anchored as an
|
|
104
|
+
* inline comment on the current diff. `diffFiles` is the per-file diff
|
|
105
|
+
* (as returned by `gh api repos/.../pulls/.../files`).
|
|
106
|
+
*/
|
|
107
|
+
export function findingAnchorable(finding, diffFiles) {
|
|
108
|
+
if (!finding.file || !finding.line)
|
|
109
|
+
return false;
|
|
110
|
+
const file = diffFiles.find((f) => f.filename === finding.file);
|
|
111
|
+
if (!file)
|
|
112
|
+
return false;
|
|
113
|
+
return parseHeadLines(file.patch).has(finding.line);
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Context extraction for the verifier (consumed in Wave 5b)
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
/**
|
|
119
|
+
* Builds the `{codeBefore, codeAfter, diffAtAnchor?}` triple Wave 5b's
|
|
120
|
+
* auto-resolve verifier consumes for a single finding, pulled out of the
|
|
121
|
+
* unified-diff hunk that touches the finding's line.
|
|
122
|
+
*
|
|
123
|
+
* Works entirely from the in-hand patch — no fresh API calls. The patch
|
|
124
|
+
* already encodes BEFORE (lines with `-` or context) and AFTER (lines with
|
|
125
|
+
* `+` or context) for every hunk the PR touches.
|
|
126
|
+
*
|
|
127
|
+
* Returns empty strings for `codeBefore`/`codeAfter` when the finding's
|
|
128
|
+
* file isn't in the diff at all. The verifier sees "unchanged" and
|
|
129
|
+
* typically returns NOT_ADDRESSED / UNCERTAIN, which is the correct
|
|
130
|
+
* behavior — an unchanged file isn't a fix.
|
|
131
|
+
*/
|
|
132
|
+
export function extractAnchorContext(finding, diffFiles) {
|
|
133
|
+
if (!finding.file || !finding.line) {
|
|
134
|
+
return { codeBefore: '', codeAfter: '' };
|
|
135
|
+
}
|
|
136
|
+
const file = diffFiles.find((f) => f.filename === finding.file);
|
|
137
|
+
if (!file || !file.patch) {
|
|
138
|
+
return { codeBefore: '', codeAfter: '' };
|
|
139
|
+
}
|
|
140
|
+
// Locate the hunk whose new-side range contains finding.line.
|
|
141
|
+
const hunkHeaderRe = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/;
|
|
142
|
+
const patchLines = file.patch.split('\n');
|
|
143
|
+
let currentHunkStart = -1;
|
|
144
|
+
for (let i = 0; i < patchLines.length; i++) {
|
|
145
|
+
const m = patchLines[i].match(hunkHeaderRe);
|
|
146
|
+
if (!m)
|
|
147
|
+
continue;
|
|
148
|
+
const newStart = Number.parseInt(m[3], 10);
|
|
149
|
+
const newLen = m[4] !== undefined ? Number.parseInt(m[4], 10) : 1;
|
|
150
|
+
if (finding.line >= newStart &&
|
|
151
|
+
finding.line < newStart + Math.max(newLen, 1)) {
|
|
152
|
+
currentHunkStart = i;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (currentHunkStart < 0) {
|
|
157
|
+
return { codeBefore: '', codeAfter: '' };
|
|
158
|
+
}
|
|
159
|
+
const before = [];
|
|
160
|
+
const after = [];
|
|
161
|
+
const hunkBody = [patchLines[currentHunkStart]];
|
|
162
|
+
for (let i = currentHunkStart + 1; i < patchLines.length; i++) {
|
|
163
|
+
const line = patchLines[i];
|
|
164
|
+
if (line.match(hunkHeaderRe))
|
|
165
|
+
break;
|
|
166
|
+
hunkBody.push(line);
|
|
167
|
+
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
168
|
+
after.push(line.slice(1));
|
|
169
|
+
}
|
|
170
|
+
else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
171
|
+
before.push(line.slice(1));
|
|
172
|
+
}
|
|
173
|
+
else if (line.startsWith('\\')) {
|
|
174
|
+
// No-newline marker — skip.
|
|
175
|
+
}
|
|
176
|
+
else if (line.length > 0) {
|
|
177
|
+
// Context line — appears on both sides.
|
|
178
|
+
const stripped = line.startsWith(' ') ? line.slice(1) : line;
|
|
179
|
+
before.push(stripped);
|
|
180
|
+
after.push(stripped);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
codeBefore: before.join('\n'),
|
|
185
|
+
codeAfter: after.join('\n'),
|
|
186
|
+
diffAtAnchor: hunkBody.join('\n'),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Comment body rendering
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
/**
|
|
193
|
+
* Body for a single inline-thread comment. Includes a hidden HTML marker
|
|
194
|
+
* carrying `findingId` so Wave 5b's auto-resolve can recover the canonical
|
|
195
|
+
* id from the thread's first comment body without persisting state.
|
|
196
|
+
*/
|
|
197
|
+
export function renderThreadBody(finding) {
|
|
198
|
+
const id = findingId(finding);
|
|
199
|
+
const severityBadge = finding.severity === 'critical' ? '🔴' : '🟡';
|
|
200
|
+
const header = `${severityBadge} \`${finding.skill}\``;
|
|
201
|
+
const body = finding.summary.trim();
|
|
202
|
+
const reasoning = finding.reasoning ? `\n\n${finding.reasoning.trim()}` : '';
|
|
203
|
+
return `<!-- finding-id: ${id} -->\n${header}\n\n${body}${reasoning}`;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Extracts the hashed `findingId` from a thread comment body that was
|
|
207
|
+
* rendered by `renderThreadBody`. Returns null when the marker is absent
|
|
208
|
+
* (a manual reply, or a comment posted by a different tool).
|
|
209
|
+
*
|
|
210
|
+
* Used by Wave 5b's auto-resolve to re-derive finding ids from existing
|
|
211
|
+
* GitHub threads on a stateless re-run.
|
|
212
|
+
*/
|
|
213
|
+
export function extractFindingIdFromBody(body) {
|
|
214
|
+
// Anchor to start-of-string so a user reply that quotes the marker can't be
|
|
215
|
+
// misattributed as bot-authored. `renderThreadBody` always places the marker
|
|
216
|
+
// on line 1 — anything else is suspect input.
|
|
217
|
+
const m = body.match(/^<!--\s*finding-id:\s*([a-f0-9]{16})\s*-->/);
|
|
218
|
+
return m ? m[1] : null;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Wave 5b — invert `renderThreadBody` to recover the original finding
|
|
222
|
+
* tuple from a bot-authored thread comment. The fix-push auto-resolve
|
|
223
|
+
* CLI verb uses this to reconstruct `PriorFinding` from the GraphQL
|
|
224
|
+
* `reviewThreads` response without persistent state.
|
|
225
|
+
*
|
|
226
|
+
* Returns null when the body doesn't match the canonical shape
|
|
227
|
+
* `<!-- finding-id: XX -->\n🔴|🟡 \`skill\`\n\nsummary[\n\nreasoning]`.
|
|
228
|
+
* Same start-anchored posture as `extractFindingIdFromBody` for anti-
|
|
229
|
+
* injection: a user reply that quotes the marker mid-body won't be
|
|
230
|
+
* misattributed as bot-authored.
|
|
231
|
+
*/
|
|
232
|
+
export function parseThreadBody(body) {
|
|
233
|
+
// Marker (line 1), header (line 2: badge + backticked skill), blank,
|
|
234
|
+
// summary, optional blank + reasoning. The header line shape:
|
|
235
|
+
// `🔴 \`critical-issues-only\`` or `🟡 \`magic-numbers\``
|
|
236
|
+
const re = /^<!--\s*finding-id:\s*([a-f0-9]{16})\s*-->\n(🔴|🟡)\s+`([^`]+)`\n\n([\s\S]+?)(?:\n\n([\s\S]+))?$/;
|
|
237
|
+
const m = body.match(re);
|
|
238
|
+
if (!m)
|
|
239
|
+
return null;
|
|
240
|
+
const [, findingId, badge, skill, summary, reasoning] = m;
|
|
241
|
+
const severity = badge === '🔴' ? 'critical' : 'minor';
|
|
242
|
+
const out = {
|
|
243
|
+
findingId: findingId,
|
|
244
|
+
severity,
|
|
245
|
+
skill: skill,
|
|
246
|
+
summary: summary.trim(),
|
|
247
|
+
};
|
|
248
|
+
if (reasoning && reasoning.trim())
|
|
249
|
+
out.reasoning = reasoning.trim();
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Partitions a flat findings list into the `comments[]` body for
|
|
254
|
+
* `POST /repos/:owner/:repo/pulls/:n/reviews` + the findings the caller
|
|
255
|
+
* should surface elsewhere (summary comment for `skipped`; never for
|
|
256
|
+
* `preexisting`).
|
|
257
|
+
*
|
|
258
|
+
* Pure function — no I/O. The CLI verb uses this to compute the API body;
|
|
259
|
+
* the App can use it after Wave 5c migration to share the same partitioning.
|
|
260
|
+
*/
|
|
261
|
+
export function planInlineThreads(findings, diffFiles) {
|
|
262
|
+
const comments = [];
|
|
263
|
+
const skipped = [];
|
|
264
|
+
const preexisting = [];
|
|
265
|
+
for (const f of findings) {
|
|
266
|
+
if (f.severity === 'preexisting') {
|
|
267
|
+
preexisting.push(f);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (!findingAnchorable(f, diffFiles)) {
|
|
271
|
+
skipped.push(f);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
comments.push({
|
|
275
|
+
path: f.file,
|
|
276
|
+
line: f.line,
|
|
277
|
+
side: 'RIGHT',
|
|
278
|
+
body: renderThreadBody(f),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return { comments, skipped, preexisting };
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// GraphQL operations — pinned to module scope so callers (and tests) can
|
|
285
|
+
// reference the same query strings as the App.
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// Wave 5b update — `author { login }` added so the resolve-threads CLI
|
|
288
|
+
// verb can filter to bot-authored threads. Without this, the bot-filter
|
|
289
|
+
// reads `comments[0].author?.login` as undefined and accepts everything,
|
|
290
|
+
// which would feed user-authored threads (that happen to start with the
|
|
291
|
+
// marker) into the verifier. Additive change — Wave 5a consumers
|
|
292
|
+
// (post-inline-threads) didn't read `author` so they're unaffected.
|
|
293
|
+
export const REVIEW_THREADS_QUERY = `
|
|
294
|
+
query ReviewThreads($owner: String!, $repo: String!, $pr: Int!) {
|
|
295
|
+
repository(owner: $owner, name: $repo) {
|
|
296
|
+
pullRequest(number: $pr) {
|
|
297
|
+
reviewThreads(first: 100) {
|
|
298
|
+
nodes {
|
|
299
|
+
id
|
|
300
|
+
isResolved
|
|
301
|
+
comments(first: 100) {
|
|
302
|
+
nodes {
|
|
303
|
+
databaseId
|
|
304
|
+
path
|
|
305
|
+
line
|
|
306
|
+
originalLine
|
|
307
|
+
body
|
|
308
|
+
author {
|
|
309
|
+
login
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
// Includes `body` (unlike the App's lighter state query, which omits it) so
|
|
320
|
+
// Wave 5b's auto-resolve can re-derive the `findingId` from the
|
|
321
|
+
// `<!-- finding-id: ... -->` marker on each thread's first comment WITHOUT
|
|
322
|
+
// persisting state between fix-pushes. The npm workflow is stateless; the
|
|
323
|
+
// body field is the substitute for Redis-backed thread-id correlation.
|
|
324
|
+
export const REVIEW_THREADS_STATE_QUERY = `
|
|
325
|
+
query ReviewThreadStates($owner: String!, $repo: String!, $pr: Int!) {
|
|
326
|
+
repository(owner: $owner, name: $repo) {
|
|
327
|
+
pullRequest(number: $pr) {
|
|
328
|
+
reviewThreads(first: 100) {
|
|
329
|
+
nodes {
|
|
330
|
+
id
|
|
331
|
+
isResolved
|
|
332
|
+
comments(first: 1) {
|
|
333
|
+
nodes {
|
|
334
|
+
databaseId
|
|
335
|
+
body
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
`;
|
|
344
|
+
export const RESOLVE_THREAD_MUTATION = `
|
|
345
|
+
mutation ResolveThread($threadId: ID!) {
|
|
346
|
+
resolveReviewThread(input: { threadId: $threadId }) {
|
|
347
|
+
thread {
|
|
348
|
+
isResolved
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
`;
|
|
353
|
+
// Wave 5b — post a reply on a review thread (used to attach the
|
|
354
|
+
// auto-resolve marker explaining the verifier's verdict) BEFORE
|
|
355
|
+
// calling `RESOLVE_THREAD_MUTATION`. The reply is visible in the PR
|
|
356
|
+
// timeline so reviewers can audit why the bot auto-resolved.
|
|
357
|
+
export const ADD_REPLY_MUTATION = `
|
|
358
|
+
mutation AddThreadReply($threadId: ID!, $body: String!) {
|
|
359
|
+
addPullRequestReviewThreadReply(
|
|
360
|
+
input: { pullRequestReviewThreadId: $threadId, body: $body }
|
|
361
|
+
) {
|
|
362
|
+
comment {
|
|
363
|
+
id
|
|
364
|
+
body
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
`;
|
|
369
|
+
//# sourceMappingURL=inline-threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-threads.js","sourceRoot":"","sources":["../../src/core/inline-threads.ts"],"names":[],"mappings":"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"}
|