@codraoss/core 0.9.4 → 0.9.12
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/dist/{chunk-XD6CGYHO.js → chunk-4XEMXRNG.js} +62 -39
- package/dist/chunk-4XEMXRNG.js.map +1 -0
- package/dist/{chunk-ILZUCIZH.js → chunk-EQL5OOEF.js} +7 -7
- package/dist/chunk-EQL5OOEF.js.map +1 -0
- package/dist/{chunk-RQ5S67XB.js → chunk-FKRCBUWZ.js} +8 -2
- package/dist/chunk-FKRCBUWZ.js.map +1 -0
- package/dist/{chunk-JC74XH7Q.js → chunk-GDX2ZEBA.js} +1 -1
- package/dist/chunk-GDX2ZEBA.js.map +1 -0
- package/dist/{chunk-OIQRTDYR.js → chunk-I5S3NBFL.js} +20 -4
- package/dist/chunk-I5S3NBFL.js.map +1 -0
- package/dist/{chunk-U3VKVDZ7.js → chunk-IKVUW4WC.js} +92 -19
- package/dist/chunk-IKVUW4WC.js.map +1 -0
- package/dist/{chunk-R2HRWFRH.js → chunk-JAF7JIZQ.js} +1 -1
- package/dist/chunk-JAF7JIZQ.js.map +1 -0
- package/dist/{chunk-V3RF6GQW.js → chunk-KTDBDKL5.js} +1 -1
- package/dist/chunk-KTDBDKL5.js.map +1 -0
- package/dist/{chunk-MMVQPGK3.js → chunk-ONJZXUQT.js} +112 -24
- package/dist/chunk-ONJZXUQT.js.map +1 -0
- package/dist/{chunk-PVXFURDR.js → chunk-QPPLO2YV.js} +2 -2
- package/dist/chunk-QPPLO2YV.js.map +1 -0
- package/dist/{chunk-FGXA7JNR.js → chunk-W3RY755V.js} +14 -2
- package/dist/chunk-W3RY755V.js.map +1 -0
- package/dist/{chunk-NSTV4TRP.js → chunk-XPYVLHSX.js} +1 -1
- package/dist/chunk-XPYVLHSX.js.map +1 -0
- package/dist/{chunk-WSK45HW4.js → chunk-Z5B5X7QP.js} +1 -1
- package/dist/chunk-Z5B5X7QP.js.map +1 -0
- package/dist/claim-checks.d.ts +8 -13
- package/dist/claim-checks.js +2 -2
- package/dist/diff/index.d.ts +3 -3
- package/dist/diff/index.js +2 -1
- package/dist/fingerprint.js +1 -1
- package/dist/formatter-CDFpqbDP.d.ts +24 -0
- package/dist/formatter.d.ts +29 -0
- package/dist/formatter.js +111 -0
- package/dist/formatter.js.map +1 -0
- package/dist/{index-BTDWXDck.d.ts → index-CrtHz4vN.d.ts} +3 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.js +239 -62
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/{model-gRDTk8yk.d.ts → model-BkqfyVh9.d.ts} +27 -6
- package/dist/model-output/index.d.ts +2 -2
- package/dist/model-output/index.js +9 -9
- package/dist/ports/index.d.ts +22 -18
- package/dist/ports/index.js +1 -1
- package/dist/{position-h_jdn6ZH.d.ts → position-zZl8KGVu.d.ts} +1 -1
- package/dist/prompts/file-review.d.ts +3 -3
- package/dist/prompts/file-review.js +11 -5
- package/dist/prompts/languages.d.ts +2 -1
- package/dist/prompts/languages.js +5 -3
- package/dist/prompts/verify.d.ts +1 -1
- package/dist/prompts/verify.js +1 -1
- package/dist/rules/detect.d.ts +1 -1
- package/dist/rules/detect.js +5 -5
- package/dist/rules/table.js +1 -1
- package/dist/token-tracker.js +2 -2
- package/package.json +10 -2
- package/dist/chunk-FGXA7JNR.js.map +0 -1
- package/dist/chunk-ILZUCIZH.js.map +0 -1
- package/dist/chunk-JC74XH7Q.js.map +0 -1
- package/dist/chunk-MMVQPGK3.js.map +0 -1
- package/dist/chunk-NSTV4TRP.js.map +0 -1
- package/dist/chunk-OIQRTDYR.js.map +0 -1
- package/dist/chunk-PVXFURDR.js.map +0 -1
- package/dist/chunk-R2HRWFRH.js.map +0 -1
- package/dist/chunk-RQ5S67XB.js.map +0 -1
- package/dist/chunk-U3VKVDZ7.js.map +0 -1
- package/dist/chunk-V3RF6GQW.js.map +0 -1
- package/dist/chunk-WSK45HW4.js.map +0 -1
- package/dist/chunk-XD6CGYHO.js.map +0 -1
package/dist/logger.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { RepoConfig } from '@codraoss/schema';
|
|
2
|
-
import { F as FileDiff } from './position-
|
|
3
|
-
import { p as parseFileReviewResponse, B as BatchReviewResult } from './index-
|
|
2
|
+
import { F as FileDiff } from './position-zZl8KGVu.js';
|
|
3
|
+
import { p as parseFileReviewResponse, B as BatchReviewResult } from './index-CrtHz4vN.js';
|
|
4
4
|
import { VerifyCandidate } from './prompts/verify.js';
|
|
5
5
|
|
|
6
|
+
declare function changelogExcerptFromDiff(files: readonly FileDiff[]): string | null;
|
|
7
|
+
declare function wantsFileContext(file: Pick<FileDiff, 'lineCount' | 'isNew' | 'isDeleted' | 'isBinary'> & {
|
|
8
|
+
hunks?: FileDiff['hunks'];
|
|
9
|
+
}, fullFileContext: boolean, gate?: {
|
|
10
|
+
compactPrompt?: boolean;
|
|
11
|
+
}): boolean;
|
|
12
|
+
|
|
6
13
|
declare function generatorFindingCap(maxComments: number): number;
|
|
7
|
-
|
|
8
|
-
declare function
|
|
14
|
+
/** Internal candidate cap upstream of posting; falls back for job snapshots queued before this field existed. */
|
|
15
|
+
declare function reviewBreadth(config: Pick<RepoConfig['review'], 'max_comments'> & {
|
|
16
|
+
review_breadth?: number;
|
|
17
|
+
}): number;
|
|
18
|
+
declare function buildReviewResponseSchema(findingCap: number): ModelResponseSchema;
|
|
19
|
+
declare function buildBatchReviewResponseSchema(findingCap: number, fileCount: number): ModelResponseSchema;
|
|
9
20
|
declare function buildFileReviewSystemPromptBase(opts?: {
|
|
10
21
|
multiFile?: boolean;
|
|
22
|
+
fileContext?: boolean;
|
|
11
23
|
}): string;
|
|
12
24
|
declare const fileReviewSystemPromptBase: string;
|
|
13
25
|
declare function buildFileReviewSystemPrompt(config: RepoConfig['review'], languagePersona?: string, opts?: {
|
|
14
26
|
multiFile?: boolean;
|
|
27
|
+
fileContext?: boolean;
|
|
15
28
|
}): string;
|
|
16
29
|
type RejectedExemplar = {
|
|
17
30
|
title: string;
|
|
@@ -19,8 +32,10 @@ type RejectedExemplar = {
|
|
|
19
32
|
};
|
|
20
33
|
declare function buildFileReviewPrompts(input: {
|
|
21
34
|
file: FileDiff;
|
|
35
|
+
fileContext?: string | null;
|
|
22
36
|
prTitle: string | null;
|
|
23
37
|
prDescription: string | null;
|
|
38
|
+
changelogExcerpt?: string | null;
|
|
24
39
|
config: RepoConfig['review'];
|
|
25
40
|
rejectedExemplars?: readonly RejectedExemplar[];
|
|
26
41
|
}): {
|
|
@@ -31,6 +46,7 @@ declare function buildBatchReviewPrompts(input: {
|
|
|
31
46
|
files: readonly FileDiff[];
|
|
32
47
|
prTitle: string | null;
|
|
33
48
|
prDescription: string | null;
|
|
49
|
+
changelogExcerpt?: string | null;
|
|
34
50
|
config: RepoConfig['review'];
|
|
35
51
|
rejectedExemplars?: readonly RejectedExemplar[];
|
|
36
52
|
}): {
|
|
@@ -46,7 +62,7 @@ type ModelResponse = {
|
|
|
46
62
|
outputTokens: number;
|
|
47
63
|
modelUsed: string;
|
|
48
64
|
provider: string;
|
|
49
|
-
degraded?: 'schema-dropped';
|
|
65
|
+
degraded?: 'schema-dropped' | 'schema-dropped-catchall' | 'truncated';
|
|
50
66
|
};
|
|
51
67
|
type ModelResponseSchema = {
|
|
52
68
|
name: string;
|
|
@@ -61,8 +77,10 @@ type FileReviewOutcome = ModelResponse & {
|
|
|
61
77
|
interface ReviewModel {
|
|
62
78
|
reviewFile(params: {
|
|
63
79
|
file: FileDiff;
|
|
80
|
+
fileContext?: string | null;
|
|
64
81
|
prTitle: string | null;
|
|
65
82
|
prDescription: string | null;
|
|
83
|
+
changelogExcerpt?: string | null;
|
|
66
84
|
config: RepoConfig;
|
|
67
85
|
totalLineCount: number;
|
|
68
86
|
compactPrompt?: boolean;
|
|
@@ -72,6 +90,7 @@ interface ReviewModel {
|
|
|
72
90
|
files: readonly FileDiff[];
|
|
73
91
|
prTitle: string | null;
|
|
74
92
|
prDescription: string | null;
|
|
93
|
+
changelogExcerpt?: string | null;
|
|
75
94
|
config: RepoConfig;
|
|
76
95
|
totalLineCount: number;
|
|
77
96
|
rejectedExemplars?: readonly RejectedExemplar[];
|
|
@@ -81,8 +100,10 @@ interface ReviewModel {
|
|
|
81
100
|
}>;
|
|
82
101
|
submitReviewBatch(params: {
|
|
83
102
|
file: FileDiff;
|
|
103
|
+
fileContext?: string | null;
|
|
84
104
|
prTitle: string | null;
|
|
85
105
|
prDescription: string | null;
|
|
106
|
+
changelogExcerpt?: string | null;
|
|
86
107
|
config: RepoConfig;
|
|
87
108
|
totalLineCount: number;
|
|
88
109
|
compactPrompt?: boolean;
|
|
@@ -114,4 +135,4 @@ interface ModelErrorClassifier {
|
|
|
114
135
|
nextChainIndexOf(error: unknown): number | null;
|
|
115
136
|
}
|
|
116
137
|
|
|
117
|
-
export { type FileReviewOutcome as F, type ModelErrorClassifier as M, type ReviewModel as R, type ModelResponse as a, type ModelResponseSchema as b, type RejectedExemplar as c, buildBatchReviewPrompts as d, buildBatchReviewResponseSchema as e, buildFileReviewPrompts as f, buildFileReviewSystemPrompt as g, buildFileReviewSystemPromptBase as h, buildReviewResponseSchema as i,
|
|
138
|
+
export { type FileReviewOutcome as F, type ModelErrorClassifier as M, type ReviewModel as R, type ModelResponse as a, type ModelResponseSchema as b, type RejectedExemplar as c, buildBatchReviewPrompts as d, buildBatchReviewResponseSchema as e, buildFileReviewPrompts as f, buildFileReviewSystemPrompt as g, buildFileReviewSystemPromptBase as h, buildReviewResponseSchema as i, changelogExcerptFromDiff as j, fileReviewSystemPromptBase as k, generatorFindingCap as l, reviewBreadth as m, renderFileDiff as r, wantsFileContext as w };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '@codraoss/schema';
|
|
2
2
|
import 'zod';
|
|
3
|
-
import '../position-
|
|
4
|
-
export { d as BatchParseStats, B as BatchReviewResult, e as BinAmbiguity, f as FileReviewPayload, G as GroundedFileReview, g as GroundingOptions, N as NON_ANSWER_MAX_RESPONSE_CHARS, h as NON_ANSWER_MIN_DIFF_LINES, R as RawBatchPayload, i as dedupeFindings, j as groundParsedFindings, k as isNonAnswerReview, l as parseBatchReviewResponse, p as parseFileReviewResponse, m as parseRawBatchPayload, s as samePath } from '../index-
|
|
3
|
+
import '../position-zZl8KGVu.js';
|
|
4
|
+
export { d as BatchParseStats, B as BatchReviewResult, e as BinAmbiguity, f as FileReviewPayload, G as GroundedFileReview, g as GroundingOptions, N as NON_ANSWER_MAX_RESPONSE_CHARS, h as NON_ANSWER_MIN_DIFF_LINES, R as RawBatchPayload, i as dedupeFindings, j as groundParsedFindings, k as isNonAnswerReview, l as parseBatchReviewResponse, p as parseFileReviewResponse, m as parseRawBatchPayload, s as samePath } from '../index-CrtHz4vN.js';
|
|
@@ -6,18 +6,18 @@ import {
|
|
|
6
6
|
parseFileReviewResponse,
|
|
7
7
|
parseRawBatchPayload,
|
|
8
8
|
samePath
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-4XEMXRNG.js";
|
|
10
|
+
import "../chunk-I5S3NBFL.js";
|
|
11
|
+
import "../chunk-ONJZXUQT.js";
|
|
12
|
+
import "../chunk-FKRCBUWZ.js";
|
|
13
|
+
import "../chunk-KTDBDKL5.js";
|
|
13
14
|
import {
|
|
14
15
|
NON_ANSWER_MAX_RESPONSE_CHARS,
|
|
15
16
|
NON_ANSWER_MIN_DIFF_LINES
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-
|
|
18
|
-
import "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-OIQRTDYR.js";
|
|
17
|
+
} from "../chunk-W3RY755V.js";
|
|
18
|
+
import "../chunk-IKVUW4WC.js";
|
|
19
|
+
import "../chunk-XPYVLHSX.js";
|
|
20
|
+
import "../chunk-Z5B5X7QP.js";
|
|
21
21
|
export {
|
|
22
22
|
NON_ANSWER_MAX_RESPONSE_CHARS,
|
|
23
23
|
NON_ANSWER_MIN_DIFF_LINES,
|
package/dist/ports/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { JobSummary, RepoConfig, ParsedReviewComment, ClaimType, ReviewSettings, ReviewJobMessage } from '@codraoss/schema';
|
|
2
|
-
import { R as ReviewModel, M as ModelErrorClassifier } from '../model-
|
|
3
|
-
export { F as FileReviewOutcome, a as ModelResponse, b as ModelResponseSchema } from '../model-
|
|
2
|
+
import { R as ReviewModel, M as ModelErrorClassifier } from '../model-BkqfyVh9.js';
|
|
3
|
+
export { F as FileReviewOutcome, a as ModelResponse, b as ModelResponseSchema } from '../model-BkqfyVh9.js';
|
|
4
|
+
import { a as ReviewFormatter } from '../formatter-CDFpqbDP.js';
|
|
5
|
+
export { R as ReviewOverviewInput } from '../formatter-CDFpqbDP.js';
|
|
4
6
|
import { TokenTracker } from '../token-tracker.js';
|
|
5
7
|
export { Logger } from '../logger.js';
|
|
6
|
-
import '../position-
|
|
7
|
-
import '../index-
|
|
8
|
+
import '../position-zZl8KGVu.js';
|
|
9
|
+
import '../index-CrtHz4vN.js';
|
|
8
10
|
import 'zod';
|
|
9
11
|
import '../prompts/verify.js';
|
|
10
12
|
|
|
@@ -155,6 +157,8 @@ type FileReviewRow = {
|
|
|
155
157
|
withheld_counts: {
|
|
156
158
|
evidence?: number;
|
|
157
159
|
claimDenied?: number;
|
|
160
|
+
contextOnly?: number;
|
|
161
|
+
absenceRefuted?: number;
|
|
158
162
|
};
|
|
159
163
|
batch_size: number | null;
|
|
160
164
|
};
|
|
@@ -183,7 +187,10 @@ type BulkFileReviewInput = {
|
|
|
183
187
|
withheldCounts?: {
|
|
184
188
|
evidence: number;
|
|
185
189
|
claimDenied: number;
|
|
190
|
+
contextOnly?: number;
|
|
191
|
+
absenceRefuted?: number;
|
|
186
192
|
} | null;
|
|
193
|
+
degraded?: string | null;
|
|
187
194
|
batchSize: number;
|
|
188
195
|
};
|
|
189
196
|
interface FileReviewStore {
|
|
@@ -207,7 +214,10 @@ interface FileReviewStore {
|
|
|
207
214
|
withheldCounts?: {
|
|
208
215
|
evidence: number;
|
|
209
216
|
claimDenied: number;
|
|
217
|
+
contextOnly?: number;
|
|
218
|
+
absenceRefuted?: number;
|
|
210
219
|
} | null;
|
|
220
|
+
degraded?: string | null;
|
|
211
221
|
asyncRequestId?: string | null;
|
|
212
222
|
asyncModel?: string | null;
|
|
213
223
|
}): Promise<void>;
|
|
@@ -324,6 +334,8 @@ interface ReviewGitProvider {
|
|
|
324
334
|
getPullRequest(owner: string, repo: string, prNumber: number): Promise<PullRequestRecord>;
|
|
325
335
|
getPullRequestDiff(owner: string, repo: string, prNumber: number): Promise<string>;
|
|
326
336
|
getCompareDiff(owner: string, repo: string, base: string, head: string): Promise<string>;
|
|
337
|
+
/** File content at `ref`, or null if unavailable. Optional: backs opt-in file-context enrichment. */
|
|
338
|
+
getRepoFile?(owner: string, repo: string, path: string, ref?: string): Promise<string | null>;
|
|
327
339
|
createCheckRun(owner: string, repo: string, params: {
|
|
328
340
|
headSha: string;
|
|
329
341
|
title: string;
|
|
@@ -351,23 +363,14 @@ interface ReviewGitProvider {
|
|
|
351
363
|
} | null>;
|
|
352
364
|
ensureLabel(owner: string, repo: string, name: string, color: string): Promise<unknown>;
|
|
353
365
|
addIssueLabels(owner: string, repo: string, prNumber: number, labels: string[]): Promise<unknown>;
|
|
366
|
+
/** Optional: a provider or test double without it simply does not react. */
|
|
367
|
+
addIssueReaction?(owner: string, repo: string, prNumber: number, content: '+1'): Promise<unknown>;
|
|
354
368
|
removeIssueLabelsIfPresent(owner: string, repo: string, prNumber: number, labels: string[]): Promise<unknown>;
|
|
355
369
|
}
|
|
356
370
|
interface GitProviderFactory {
|
|
357
371
|
forInstallation(installationId: string): ReviewGitProvider;
|
|
358
372
|
}
|
|
359
373
|
|
|
360
|
-
interface ReviewFormatter {
|
|
361
|
-
toReviewEvent(verdict: 'approve' | 'comment'): 'APPROVE' | 'COMMENT';
|
|
362
|
-
summarizeVerdict(comments: ParsedReviewComment[], hasFailures: boolean): {
|
|
363
|
-
verdict: 'approve' | 'comment';
|
|
364
|
-
errors: number;
|
|
365
|
-
warnings: number;
|
|
366
|
-
};
|
|
367
|
-
formatInlineComment(comment: ParsedReviewComment): string;
|
|
368
|
-
formatReviewOverview(commitSha: string, botUsername: string): string;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
374
|
type ReviewTelemetryEvent = {
|
|
372
375
|
linesReviewed: number;
|
|
373
376
|
findingsReported: number;
|
|
@@ -433,7 +436,7 @@ interface KeyValueStore {
|
|
|
433
436
|
expirationTtl?: number;
|
|
434
437
|
}): Promise<void>;
|
|
435
438
|
get(key: string, type: 'json'): Promise<unknown>;
|
|
436
|
-
get(key: string, type
|
|
439
|
+
get(key: string, type?: 'text'): Promise<string | null>;
|
|
437
440
|
delete(key: string): Promise<void>;
|
|
438
441
|
}
|
|
439
442
|
|
|
@@ -441,6 +444,7 @@ interface QueueProducer<T> {
|
|
|
441
444
|
send(message: T, options?: {
|
|
442
445
|
delaySeconds?: number;
|
|
443
446
|
}): Promise<void>;
|
|
447
|
+
deleteJob?(jobId: string): Promise<void>;
|
|
444
448
|
}
|
|
445
449
|
|
|
446
450
|
interface JobOrchestrator {
|
|
@@ -485,7 +489,7 @@ declare class InMemoryKV implements KeyValueStore {
|
|
|
485
489
|
put(key: string, value: string, options?: {
|
|
486
490
|
expirationTtl?: number;
|
|
487
491
|
}): Promise<void>;
|
|
488
|
-
get(key: string, type
|
|
492
|
+
get(key: string, type?: 'json' | 'text'): Promise<any>;
|
|
489
493
|
delete(key: string): Promise<void>;
|
|
490
494
|
}
|
|
491
495
|
declare class InMemoryQueue<T> implements QueueProducer<T> {
|
|
@@ -509,4 +513,4 @@ declare class InMemorySessionStore implements SessionStore {
|
|
|
509
513
|
renewSession(token: string): Promise<void>;
|
|
510
514
|
}
|
|
511
515
|
|
|
512
|
-
export { type AuthorizationResult, type BulkFileReviewInput, type Clock, type DashboardSessionUser, type FileReviewRow, type FileReviewStore, type GitProviderFactory, type IdGenerator, type IdentityProvider, InMemoryKV, InMemoryOrchestrator, InMemoryQueue, InMemorySessionStore, type InstanceIdStore, type JobLeaseClaim, type JobOrchestrator, type JobRow, type JobStore, type KeyValueStore, type KvStore, type LearningStore, type ModelConfigReader, ModelErrorClassifier, type PersistedReviewJob, type PullRequestRecord, type QueueProducer, type RepoConfigLoader, type RepoConfigStore, type ReviewComment,
|
|
516
|
+
export { type AuthorizationResult, type BulkFileReviewInput, type Clock, type DashboardSessionUser, type FileReviewRow, type FileReviewStore, type GitProviderFactory, type IdGenerator, type IdentityProvider, InMemoryKV, InMemoryOrchestrator, InMemoryQueue, InMemorySessionStore, type InstanceIdStore, type JobLeaseClaim, type JobOrchestrator, type JobRow, type JobStore, type KeyValueStore, type KvStore, type LearningStore, type ModelConfigReader, ModelErrorClassifier, type PersistedReviewJob, type PullRequestRecord, type QueueProducer, type RepoConfigLoader, type RepoConfigStore, type ReviewComment, ReviewFormatter, type ReviewGitProvider, ReviewModel, type ReviewRuntime, type ReviewSettingsReader, type ReviewTelemetryEvent, type SecretStore, type SessionStore, type SuppressedFinding, type TelemetrySink, type WebhookDeliveryReader };
|
package/dist/ports/index.js
CHANGED
|
@@ -27,4 +27,4 @@ declare function findPositionForLine(file: FileDiff, lineNumber: number): number
|
|
|
27
27
|
declare function truncateFileDiff(file: FileDiff, maxLines: number): FileDiff;
|
|
28
28
|
declare function chunkFileDiff(file: FileDiff, maxLinesPerChunk: number): FileDiff[];
|
|
29
29
|
|
|
30
|
-
export { type
|
|
30
|
+
export { type DiffHunk as D, type FileDiff as F, type DiffLine as a, type DiffLineKind as b, chunkFileDiff as c, getValidPositions as d, findPositionForLine as f, getValidNewLines as g, truncateFileDiff as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '@codraoss/schema';
|
|
2
|
-
import '../position-
|
|
3
|
-
export { c as RejectedExemplar, d as buildBatchReviewPrompts, e as buildBatchReviewResponseSchema, f as buildFileReviewPrompts, g as buildFileReviewSystemPrompt, h as buildFileReviewSystemPromptBase, i as buildReviewResponseSchema, j as
|
|
4
|
-
import '../index-
|
|
2
|
+
import '../position-zZl8KGVu.js';
|
|
3
|
+
export { c as RejectedExemplar, d as buildBatchReviewPrompts, e as buildBatchReviewResponseSchema, f as buildFileReviewPrompts, g as buildFileReviewSystemPrompt, h as buildFileReviewSystemPromptBase, i as buildReviewResponseSchema, j as changelogExcerptFromDiff, k as fileReviewSystemPromptBase, l as generatorFindingCap, r as renderFileDiff, m as reviewBreadth, w as wantsFileContext } from '../model-BkqfyVh9.js';
|
|
4
|
+
import '../index-CrtHz4vN.js';
|
|
5
5
|
import 'zod';
|
|
6
6
|
import './verify.js';
|
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
buildFileReviewSystemPrompt,
|
|
6
6
|
buildFileReviewSystemPromptBase,
|
|
7
7
|
buildReviewResponseSchema,
|
|
8
|
+
changelogExcerptFromDiff,
|
|
8
9
|
fileReviewSystemPromptBase,
|
|
9
10
|
generatorFindingCap,
|
|
10
|
-
renderFileDiff
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
renderFileDiff,
|
|
12
|
+
reviewBreadth,
|
|
13
|
+
wantsFileContext
|
|
14
|
+
} from "../chunk-ONJZXUQT.js";
|
|
15
|
+
import "../chunk-FKRCBUWZ.js";
|
|
16
|
+
import "../chunk-W3RY755V.js";
|
|
14
17
|
export {
|
|
15
18
|
buildBatchReviewPrompts,
|
|
16
19
|
buildBatchReviewResponseSchema,
|
|
@@ -18,8 +21,11 @@ export {
|
|
|
18
21
|
buildFileReviewSystemPrompt,
|
|
19
22
|
buildFileReviewSystemPromptBase,
|
|
20
23
|
buildReviewResponseSchema,
|
|
24
|
+
changelogExcerptFromDiff,
|
|
21
25
|
fileReviewSystemPromptBase,
|
|
22
26
|
generatorFindingCap,
|
|
23
|
-
renderFileDiff
|
|
27
|
+
renderFileDiff,
|
|
28
|
+
reviewBreadth,
|
|
29
|
+
wantsFileContext
|
|
24
30
|
};
|
|
25
31
|
//# sourceMappingURL=file-review.js.map
|
|
@@ -5,5 +5,6 @@ type LanguageGuideline = {
|
|
|
5
5
|
persona?: string;
|
|
6
6
|
};
|
|
7
7
|
declare function getLanguageForFile(path: string): LanguageGuideline | undefined;
|
|
8
|
+
declare function isChangelogPath(path: string): boolean;
|
|
8
9
|
|
|
9
|
-
export { type LanguageGuideline, getLanguageForFile };
|
|
10
|
+
export { type LanguageGuideline, getLanguageForFile, isChangelogPath };
|
package/dist/prompts/verify.d.ts
CHANGED
package/dist/prompts/verify.js
CHANGED
package/dist/rules/detect.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClaimType, ParsedReviewComment } from '@codraoss/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { a as DiffLine, F as FileDiff } from '../position-zZl8KGVu.js';
|
|
3
3
|
import { Rule } from './table.js';
|
|
4
4
|
|
|
5
5
|
type RuleHit = {
|
package/dist/rules/detect.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ruleHitsToComments,
|
|
3
3
|
scanFileForRuleHits
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-EQL5OOEF.js";
|
|
5
|
+
import "../chunk-JAF7JIZQ.js";
|
|
6
|
+
import "../chunk-W3RY755V.js";
|
|
7
|
+
import "../chunk-IKVUW4WC.js";
|
|
8
|
+
import "../chunk-XPYVLHSX.js";
|
|
9
9
|
export {
|
|
10
10
|
ruleHitsToComments,
|
|
11
11
|
scanFileForRuleHits
|
package/dist/rules/table.js
CHANGED
package/dist/token-tracker.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codraoss/core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
4
4
|
"description": "The Codra review engine: pure, transport- and platform-agnostic code review logic behind ports.",
|
|
5
5
|
"author": "Devarshi Shimpi",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./dist/logger.d.ts",
|
|
29
29
|
"import": "./dist/logger.js"
|
|
30
30
|
},
|
|
31
|
+
"./formatter": {
|
|
32
|
+
"types": "./dist/formatter.d.ts",
|
|
33
|
+
"import": "./dist/formatter.js"
|
|
34
|
+
},
|
|
31
35
|
"./diff": {
|
|
32
36
|
"types": "./dist/diff/index.d.ts",
|
|
33
37
|
"import": "./dist/diff/index.js"
|
|
@@ -104,6 +108,10 @@
|
|
|
104
108
|
"types": "./dist/logger.d.ts",
|
|
105
109
|
"import": "./dist/logger.js"
|
|
106
110
|
},
|
|
111
|
+
"./formatter": {
|
|
112
|
+
"types": "./dist/formatter.d.ts",
|
|
113
|
+
"import": "./dist/formatter.js"
|
|
114
|
+
},
|
|
107
115
|
"./diff": {
|
|
108
116
|
"types": "./dist/diff/index.d.ts",
|
|
109
117
|
"import": "./dist/diff/index.js"
|
|
@@ -166,7 +174,7 @@
|
|
|
166
174
|
"postpack": "node ../../scripts/swap-publish-exports.mjs restore"
|
|
167
175
|
},
|
|
168
176
|
"dependencies": {
|
|
169
|
-
"@codraoss/schema": "
|
|
177
|
+
"@codraoss/schema": "*",
|
|
170
178
|
"jsonrepair": "^3.15.0",
|
|
171
179
|
"picomatch": "^4.0.5",
|
|
172
180
|
"zod": "^4.3.6"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["// Packing & Diff Limits\nexport const PACKABLE_MAX_DIFF_LINES = 150;\nexport const BIN_TARGET_DIFF_LINES = 300;\nexport const BIN_MAX_FILES = 4;\nexport const BIN_DIFF_CHAR_BUDGET = 24_000;\nexport const DIFF_CACHE_TTL_SECONDS = 6 * 60 * 60;\n\n// Phase Control & Timers\nexport const REVIEW_CHUNK_WALL_CLOCK_MS = 12 * 60 * 1000;\nexport const JOB_LEASE_SECONDS = 15 * 60;\nexport const BUSY_RETRY_SECONDS = 60;\nexport const RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS = [30, 2 * 60, 5 * 60];\nexport const FRESH_INVOCATION_YIELD_SECONDS = 8;\nexport const ASYNC_BATCH_POLL_DELAY_SECONDS = 20;\nexport const MAX_RETRYABLE_FILE_REVIEW_FAILURES = 6;\nexport const MAX_JOB_CONTINUATIONS = 20;\nexport const MAX_FINALIZE_CONTINUATIONS = 3;\n\n// Budget & Attempts\nexport const FILE_FIXED_SUBREQUESTS = 2;\nexport const MAX_MODEL_ATTEMPTS_ESTIMATE = 4;\nexport const MISSING_FILE_ERROR = 'Model omitted this file from a batched review; retrying later.';\n\n// Model Output Parsing\nexport const MIN_DISCRIMINATING_EVIDENCE_CHARS = 8;\nexport const NON_ANSWER_MAX_RESPONSE_CHARS = 600;\nexport const NON_ANSWER_MIN_DIFF_LINES = 200;\nexport const MAX_LOGGED_JSON_CHARS = 2_000;\nexport const SEVERITY_ORDER = ['P0', 'P1', 'P2', 'P3', 'nit'] as const;\n\n// Finding Gates\nexport const VERIFY_MIN_ANSWER_RATIO = 0.6;\n\n// Rules\nexport const MAX_RULE_SCAN_ADDED_LINES = 600;\n\n// Prompts\nexport const EXEMPLAR_BLOCK_CHARS = 700;\nexport const PR_DESCRIPTION_CHARS = 2_000;\n"],"mappings":";AACO,IAAM,0BAA0B;AAChC,IAAM,wBAAwB;AAC9B,IAAM,gBAAgB;AACtB,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB,IAAI,KAAK;AAGxC,IAAM,6BAA6B,KAAK,KAAK;AAC7C,IAAM,oBAAoB,KAAK;AAC/B,IAAM,qBAAqB;AAC3B,IAAM,+CAA+C,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE;AACxE,IAAM,iCAAiC;AACvC,IAAM,iCAAiC;AACvC,IAAM,qCAAqC;AAC3C,IAAM,wBAAwB;AAC9B,IAAM,6BAA6B;AAGnC,IAAM,yBAAyB;AAC/B,IAAM,8BAA8B;AACpC,IAAM,qBAAqB;AAG3B,IAAM,oCAAoC;AAC1C,IAAM,gCAAgC;AACtC,IAAM,4BAA4B;AAClC,IAAM,wBAAwB;AAC9B,IAAM,iBAAiB,CAAC,MAAM,MAAM,MAAM,MAAM,KAAK;AAGrD,IAAM,0BAA0B;AAGhC,IAAM,4BAA4B;AAGlC,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/rules/detect.ts"],"sourcesContent":["import type { ClaimType, ParsedReviewComment } from '@codraoss/schema';\r\nimport type { DiffLine, FileDiff } from '../diff';\r\nimport { commentSyntaxFor, stripCommentsAndStrings } from '../claim-checks';\r\nimport { buildAnchorHash, buildFindingFingerprint, buildFindingFingerprintV2, normalizeDiffText } from '../fingerprint';\r\nimport { CLAIM_TYPE_CATEGORY } from '@codraoss/schema';\r\nimport { RULES, type Rule } from './table';\r\n\r\nimport { MAX_RULE_SCAN_ADDED_LINES } from '../constants';\r\n\r\nexport type RuleHit = {\r\n rule: Rule;\r\n line: DiffLine;\r\n shadow: boolean;\r\n};\r\n\r\nexport type RuleScanStats = {\r\n addedLinesScanned: number;\r\n sievePassed: number;\r\n hits: number;\r\n shadowHits: number;\r\n suppressedAsMoved: number;\r\n unstrippable: number;\r\n truncated: boolean;\r\n byRule: Record<string, number>;\r\n};\r\n\r\nexport type RuleScanResult = { hits: RuleHit[]; stats: RuleScanStats };\r\n\r\nexport type RuleScanOptions = {\r\n disabledRuleIds?: readonly string[];\r\n shadowRuleIds?: readonly string[];\r\n deniedClaimTypes?: readonly ClaimType[];\r\n};\r\n\r\nfunction extensionOf(path: string) {\r\n return path.toLowerCase().split('.').pop() ?? '';\r\n}\r\n\r\nfunction ruleApplies(rule: Rule, ext: string) {\r\n return !rule.extensions || rule.extensions.includes(ext);\r\n}\r\n\r\nexport function scanFileForRuleHits(file: FileDiff, options: RuleScanOptions = {}): RuleScanResult {\r\n const stats: RuleScanStats = {\r\n addedLinesScanned: 0,\r\n sievePassed: 0,\r\n hits: 0,\r\n shadowHits: 0,\r\n suppressedAsMoved: 0,\r\n unstrippable: 0,\r\n truncated: false,\r\n byRule: {},\r\n };\r\n const hits: RuleHit[] = [];\r\n\r\n if (file.isDeleted || file.isBinary || !file.path) return { hits, stats };\r\n\r\n const ext = extensionOf(file.path);\r\n const denied = new Set(options.deniedClaimTypes ?? []);\r\n const disabled = new Set(options.disabledRuleIds ?? []);\r\n const shadowIds = new Set(options.shadowRuleIds ?? []);\r\n\r\n const active = RULES.filter((rule) =>\r\n rule.enabled\r\n && !disabled.has(rule.id)\r\n && !denied.has(rule.claimType)\r\n && ruleApplies(rule, ext));\r\n if (active.length === 0) return { hits, stats };\r\n\r\n const triggers = [...new Set(active.flatMap((rule) => rule.triggers))];\r\n const syntax = commentSyntaxFor(file.path);\r\n\r\n for (const hunk of file.hunks) {\r\n const removed = new Set<string>();\r\n for (const l of hunk.lines) {\r\n if (l.kind === 'del') removed.add(normalizeDiffText(l.content));\r\n }\r\n\r\n for (const line of hunk.lines) {\r\n if (line.kind !== 'add') continue;\r\n if (stats.addedLinesScanned >= MAX_RULE_SCAN_ADDED_LINES) {\r\n stats.truncated = true;\r\n break;\r\n }\r\n stats.addedLinesScanned += 1;\r\n\r\n const raw = line.content;\r\n if (!triggers.some((trigger) => raw.includes(trigger))) continue;\r\n stats.sievePassed += 1;\r\n\r\n const stripped = stripCommentsAndStrings(raw, syntax);\r\n if (stripped === null) {\r\n stats.unstrippable += 1;\r\n continue;\r\n }\r\n\r\n for (const rule of active) {\r\n if (!rule.triggers.some((trigger) => raw.includes(trigger))) continue;\r\n if (!rule.pattern.test(stripped)) continue;\r\n if (rule.rejectRaw?.test(raw)) continue;\r\n\r\n if (removed.has(normalizeDiffText(raw))) {\r\n stats.suppressedAsMoved += 1;\r\n continue;\r\n }\r\n\r\n const shadow = shadowIds.has(rule.id);\r\n hits.push({ rule, line, shadow });\r\n stats.byRule[rule.id] = (stats.byRule[rule.id] ?? 0) + 1;\r\n if (shadow) stats.shadowHits += 1;\r\n else stats.hits += 1;\r\n break;\r\n }\r\n }\r\n if (stats.truncated) break;\r\n }\r\n\r\n return { hits, stats };\r\n}\r\n\r\nexport function ruleHitsToComments(file: FileDiff, result: RuleScanResult): ParsedReviewComment[] {\r\n const comments: ParsedReviewComment[] = [];\r\n for (const hit of result.hits) {\r\n if (hit.shadow) continue;\r\n\r\n const { rule, line } = hit;\r\n const anchorHash = buildAnchorHash(line.content);\r\n comments.push({\r\n path: file.path,\r\n line: line.newLineNumber ?? null,\r\n position: line.position ?? null,\r\n severity: rule.severity,\r\n category: CLAIM_TYPE_CATEGORY[rule.claimType] ?? 'quality',\r\n title: rule.title,\r\n body: rule.body,\r\n evidence: line.content,\r\n anchorHash,\r\n claimType: rule.claimType,\r\n fingerprint: buildFindingFingerprint(file.path, `${rule.title} @${anchorHash}`),\r\n fingerprintV2: buildFindingFingerprintV2(file.path, rule.claimType, anchorHash),\r\n source: 'rule' as const,\r\n ruleId: rule.id,\r\n } satisfies ParsedReviewComment);\r\n }\r\n\r\n return comments;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ports/in-memory.ts"],"sourcesContent":["import type { KeyValueStore } from './kv';\nimport type { QueueProducer } from './queue';\nimport type { JobOrchestrator } from './orchestrator';\nimport type { SessionStore, DashboardSessionUser } from './session-store';\nimport type { ReviewJobMessage } from '@codraoss/schema';\n\nexport class InMemoryKV implements KeyValueStore {\n private store = new Map<string, { value: string; expiresAt?: number }>();\n\n async put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void> {\n const expiresAt = options?.expirationTtl ? Date.now() + options.expirationTtl * 1000 : undefined;\n this.store.set(key, { value, expiresAt });\n }\n\n async get(key: string, type: 'json' | 'text'): Promise<any> {\n const entry = this.store.get(key);\n if (!entry) return null;\n if (entry.expiresAt && Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return null;\n }\n if (type === 'json') {\n try { return JSON.parse(entry.value); } catch { return null; }\n }\n return entry.value;\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n}\n\nexport class InMemoryQueue<T> implements QueueProducer<T> {\n public messages: Array<{ message: T; delaySeconds?: number }> = [];\n\n async send(message: T, options?: { delaySeconds?: number }): Promise<void> {\n this.messages.push({ message, delaySeconds: options?.delaySeconds });\n }\n}\n\nexport class InMemoryOrchestrator implements JobOrchestrator {\n public jobs: Map<string, ReviewJobMessage> = new Map();\n\n async startReviewJob(id: string, params: ReviewJobMessage): Promise<void> {\n this.jobs.set(id, params);\n }\n}\n\nexport class InMemorySessionStore implements SessionStore {\n private kv = new InMemoryKV();\n\n async createSession(session: DashboardSessionUser): Promise<string> {\n const token = Math.random().toString(36).substring(2);\n await this.kv.put(`session:${token}`, JSON.stringify(session), { expirationTtl: 60 * 60 * 24 * 7 });\n return token;\n }\n\n async readSession(token: string): Promise<DashboardSessionUser | null> {\n return this.kv.get(`session:${token}`, 'json');\n }\n\n async destroySession(token: string): Promise<void> {\n await this.kv.delete(`session:${token}`);\n }\n\n async renewSession(token: string): Promise<void> {\n const session = await this.readSession(token);\n if (session) {\n await this.kv.put(`session:${token}`, JSON.stringify(session), { expirationTtl: 60 * 60 * 24 * 7 });\n }\n }\n}\n"],"mappings":";AAMO,IAAM,aAAN,MAA0C;AAAA,EACvC,QAAQ,oBAAI,IAAmD;AAAA,EAEvE,MAAM,IAAI,KAAa,OAAe,SAAqD;AACzF,UAAM,YAAY,SAAS,gBAAgB,KAAK,IAAI,IAAI,QAAQ,gBAAgB,MAAO;AACvF,SAAK,MAAM,IAAI,KAAK,EAAE,OAAO,UAAU,CAAC;AAAA,EAC1C;AAAA,EAEA,MAAM,IAAI,KAAa,MAAqC;AAC1D,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,MAAM,aAAa,KAAK,IAAI,IAAI,MAAM,WAAW;AACnD,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,QAAI,SAAS,QAAQ;AACnB,UAAI;AAAE,eAAO,KAAK,MAAM,MAAM,KAAK;AAAA,MAAG,QAAQ;AAAE,eAAO;AAAA,MAAM;AAAA,IAC/D;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO,KAA4B;AACvC,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AACF;AAEO,IAAM,gBAAN,MAAmD;AAAA,EACjD,WAAyD,CAAC;AAAA,EAEjE,MAAM,KAAK,SAAY,SAAoD;AACzE,SAAK,SAAS,KAAK,EAAE,SAAS,cAAc,SAAS,aAAa,CAAC;AAAA,EACrE;AACF;AAEO,IAAM,uBAAN,MAAsD;AAAA,EACpD,OAAsC,oBAAI,IAAI;AAAA,EAErD,MAAM,eAAe,IAAY,QAAyC;AACxE,SAAK,KAAK,IAAI,IAAI,MAAM;AAAA,EAC1B;AACF;AAEO,IAAM,uBAAN,MAAmD;AAAA,EAChD,KAAK,IAAI,WAAW;AAAA,EAE5B,MAAM,cAAc,SAAgD;AAClE,UAAM,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACpD,UAAM,KAAK,GAAG,IAAI,WAAW,KAAK,IAAI,KAAK,UAAU,OAAO,GAAG,EAAE,eAAe,KAAK,KAAK,KAAK,EAAE,CAAC;AAClG,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,OAAqD;AACrE,WAAO,KAAK,GAAG,IAAI,WAAW,KAAK,IAAI,MAAM;AAAA,EAC/C;AAAA,EAEA,MAAM,eAAe,OAA8B;AACjD,UAAM,KAAK,GAAG,OAAO,WAAW,KAAK,EAAE;AAAA,EACzC;AAAA,EAEA,MAAM,aAAa,OAA8B;AAC/C,UAAM,UAAU,MAAM,KAAK,YAAY,KAAK;AAC5C,QAAI,SAAS;AACX,YAAM,KAAK,GAAG,IAAI,WAAW,KAAK,IAAI,KAAK,UAAU,OAAO,GAAG,EAAE,eAAe,KAAK,KAAK,KAAK,EAAE,CAAC;AAAA,IACpG;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/prompts/file-review.ts"],"sourcesContent":["import { claimTypes, type RepoConfig } from '@codraoss/schema';\r\nimport type { FileDiff } from '../diff';\r\nimport type { ModelResponseSchema } from '../ports/model';\r\nimport { getLanguageForFile } from './languages';\r\nimport {\r\n EXEMPLAR_BLOCK_CHARS,\r\n PR_DESCRIPTION_CHARS,\r\n} from '../constants';\r\n\r\nexport function generatorFindingCap(maxComments: number): number {\r\n return Math.max(1, maxComments * 2);\r\n}\r\n\r\nfunction findingItemSchema() {\r\n return {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['evidence', 'code_location', 'claim_type', 'title', 'body', 'priority'],\r\n properties: {\r\n evidence: { type: 'string' },\r\n code_location: {\r\n type: 'object',\r\n additionalProperties: false,\r\n properties: {\r\n absolute_file_path: { type: 'string' },\r\n line: { type: 'integer', minimum: 1 },\r\n line_range: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['start', 'end'],\r\n properties: {\r\n start: { type: 'integer', minimum: 1 },\r\n end: { type: 'integer', minimum: 1 },\r\n },\r\n },\r\n },\r\n anyOf: [\r\n { required: ['line'] },\r\n { required: ['line_range'] },\r\n ],\r\n },\r\n claim_type: { type: 'string', enum: [...claimTypes] },\r\n title: { type: 'string', maxLength: 100 },\r\n body: { type: 'string' },\r\n priority: { type: 'integer', minimum: 0, maximum: 4 },\r\n code_suggestion: { type: 'string' },\r\n },\r\n };\r\n}\r\n\r\nexport function buildReviewResponseSchema(maxComments: number): ModelResponseSchema {\r\n return {\r\n name: 'codra_file_review',\r\n schema: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['findings', 'overall_explanation', 'overall_correctness', 'overall_confidence_score'],\r\n properties: {\r\n findings: {\r\n type: 'array',\r\n maxItems: generatorFindingCap(maxComments),\r\n items: findingItemSchema(),\r\n },\r\n overall_explanation: { type: 'string' },\r\n overall_correctness: { type: 'string', enum: ['patch is correct', 'patch is incorrect'] },\r\n overall_confidence_score: { type: 'number', minimum: 0, maximum: 1 },\r\n },\r\n },\r\n };\r\n}\r\n\r\nexport function buildBatchReviewResponseSchema(maxComments: number, fileCount: number): ModelResponseSchema {\r\n return {\r\n name: 'codra_batch_review',\r\n schema: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['files', 'overall_confidence_score'],\r\n properties: {\r\n files: {\r\n type: 'array',\r\n maxItems: fileCount,\r\n items: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['absolute_file_path', 'findings', 'overall_explanation', 'overall_correctness'],\r\n properties: {\r\n absolute_file_path: { type: 'string' },\r\n findings: { type: 'array', items: findingItemSchema() },\r\n overall_explanation: { type: 'string' },\r\n overall_correctness: { type: 'string', enum: ['patch is correct', 'patch is incorrect'] },\r\n },\r\n },\r\n },\r\n overall_confidence_score: { type: 'number', minimum: 0, maximum: 1 },\r\n },\r\n },\r\n };\r\n}\r\n\r\nconst SINGLE_FILE_SCHEMA_FORMAT = `{\r\n \"findings\": [\r\n {\r\n \"evidence\": \"<the exact line of code from the diff this finding is about>\",\r\n \"code_location\": {\r\n \"line\": number,\r\n \"line_range\": { \"start\": number, \"end\": number }\r\n },\r\n \"claim_type\": \"<one of the claim types listed above>\",\r\n \"title\": \"<Plain title, NO tags/emoji>\",\r\n \"body\": \"<Explanation of the concrete defect and its impact>\",\r\n \"priority\": 0 | 1 | 2 | 3 | 4,\r\n \"code_suggestion\": \"Optional replacement code\"\r\n }\r\n ],\r\n \"overall_explanation\": \"Summary\",\r\n \"overall_correctness\": \"patch is correct\" | \"patch is incorrect\",\r\n \"overall_confidence_score\": number (0 to 1)\r\n}`;\r\n\r\nconst MULTI_FILE_SCHEMA_FORMAT = `{\r\n \"files\": [\r\n {\r\n \"absolute_file_path\": \"<exact path of one of the files listed below, copied character-for-character>\",\r\n \"findings\": [\r\n {\r\n \"evidence\": \"<the exact line of code from THIS file's diff this finding is about>\",\r\n \"code_location\": {\r\n \"absolute_file_path\": \"<the same path as above>\",\r\n \"line\": number,\r\n \"line_range\": { \"start\": number, \"end\": number }\r\n },\r\n \"claim_type\": \"<one of the claim types listed above>\",\r\n \"title\": \"<Plain title, NO tags/emoji>\",\r\n \"body\": \"<Explanation of the concrete defect and its impact>\",\r\n \"priority\": 0 | 1 | 2 | 3 | 4,\r\n \"code_suggestion\": \"Optional replacement code\"\r\n }\r\n ],\r\n \"overall_explanation\": \"Summary for THIS file\",\r\n \"overall_correctness\": \"patch is correct\" | \"patch is incorrect\"\r\n }\r\n ],\r\n \"overall_confidence_score\": number (0 to 1)\r\n}`;\r\n\r\nexport function buildFileReviewSystemPromptBase(opts?: { multiFile?: boolean }): string {\r\n const multi = opts?.multiFile === true;\r\n\r\n const contextScope = multi\r\n ? `- You can see ONLY the diffs below, not the whole files or the rest of the repository.\r\n- Each file below is INDEPENDENT. A finding about one file must be grounded in a line from THAT file's diff, and must be reported inside that file's entry. Never carry a claim from one file to another, and never assume two files interact unless both diffs show it.`\r\n : '- You can see ONLY the diff below, not the whole file or the rest of the repository.';\r\n\r\n const evidenceSource = multi\r\n ? `the single line of code the finding is about, copied VERBATIM from that file's diff below.`\r\n : 'the single line of code the finding is about, copied VERBATIM from the diff below.';\r\n\r\n const capRule = multi\r\n ? '4. Return at most {{MAX_COMMENTS}} findings PER FILE, most severe first. Keep each body under 160 words.'\r\n : '4. Return at most {{MAX_COMMENTS}} findings, most severe first. Keep each body under 160 words.';\r\n\r\n // presupposed clean files in every bin and reintroduced exactly the restraint language the note above\r\n const emptyRule = multi\r\n ? `5. Return exactly one entry per file listed below, in the same order, and never omit a file. Review each file's diff with the same care you would give it if it were the only file in front of you. An empty findings array is a positive claim that this diff introduces no defect, so return one only when that is true. Do not pad, and do not withhold.`\r\n : '5. If the diff genuinely introduces no defect, return an empty findings array and a short explanation. Do not pad, and do not withhold.';\r\n\r\n return `You are a world-class software engineer performing a precise, high-signal code review.\r\nYour goal is to find REAL defects (bugs, security vulnerabilities, and performance problems) introduced by the diff. Every finding must be grounded in a line you can quote from the diff.\r\n\r\n### CONTEXT EXTENDS (read carefully, this prevents false positives):\r\n${contextScope}\r\n- You cannot see which files import this one. Never predict that a change breaks callers, importers, \"other modules\" or \"external files\" -- a removed \\`export\\`, a renamed symbol or a changed signature may have no consumers at all, and you have no way to check. The same applies in reverse to a function whose body is not shown: do not assume what it does with its errors or its return value.\r\n- Assume every third-party package is at the version this project pins, and that its API is whatever that version provides. Never claim a library \"does not expose\", \"does not provide\" or \"does not support\" something; your training data predates the installed version.\r\n- Assume the language, runtime and build target are whatever the project already uses successfully. A syntax or standard-library method appearing in the diff is available in this project by construction -- the code around it already compiles and ships. Do not raise compatibility, polyfill, transpilation, engine-version or server-side-rendering concerns unless the diff itself shows the incompatibility.\r\n- Two async facts that are frequently misread. \\`return somePromise()\\` inside an \\`async\\` function IS awaited by whoever awaits that function; it is equivalent to \\`return await\\` except inside \\`try\\`/\\`finally\\`, so it is not a missing await and not a floating promise. And \\`void someAsyncCall()\\` is deliberate fire-and-forget: if the called function handles its own errors, there is no unhandled rejection to report.\r\n\r\n### WHAT TO REPORT:\r\n- Report anything a senior engineer reviewing this diff would want to investigate: a bug, a security hole, a performance problem, a resource leak, an unhandled failure, a broken invariant.\r\n- You do not need to be certain. A finding you can ground in a quoted line is worth raising; every finding is independently checked against the diff afterwards, and a wrong one is discarded at no cost to you. A defect you decline to mention is simply lost.\r\n\r\n### EVIDENCE (mandatory, a finding without it cannot be posted):\r\n- Every finding MUST include \"evidence\": ${evidenceSource}\r\n- Copy the code exactly as it appears. Do NOT include the two line-number columns or the +/- marker, do NOT paraphrase, reformat, shorten, or invent code.\r\n- If you cannot quote a specific line from the diff that exhibits the problem, you do not have a finding. Omit it.\r\n\r\n### CLAIM TYPE (required, pick the one that fits, or \"other\"):\r\n${claimTypes.join(', ')}\r\n- This is a label for the KIND of defect. It does not license the claim: only report a type if the\r\n diff actually shows it. Picking a type the code cannot exhibit makes the finding easy to discard.\r\n- If nothing fits, use \"other\". Do not stretch a label to fit.\r\n- NEVER claim that a package, action, tag or version \"does not exist\", or that a config key is invalid. You cannot know what was released after your training data, and a step pinned to a commit SHA resolves by that SHA regardless of the version written beside it. Such claims are discarded.\r\n- Label honestly. The type you choose does not affect whether a finding is accepted; an inaccurate label only makes a real defect harder to act on.\r\n\r\n### OUTPUT RULES:\r\n1. Output MUST be valid JSON, EXACTLY ONE object matching the schema below.\r\n2. DO NOT output any conversational text, source code, or diff hunks before or after the JSON.\r\n3. Prioritize by severity: 0 = P0 critical, 1 = P1 high, 2 = P2 medium, 3 = P3 low, 4 = nit (cosmetic/trivial). Set priority honestly; do not inflate. Use 4 for anything a reviewer would prefix with \"nit:\".\r\n A finding that rests on a condition you cannot check from the diff -- \"if this runs on an older engine\", \"if another module imports this\", \"depending on the caller\" -- is at most priority 3, never 0 or 1, however serious the consequence would be if the condition held. Certainty about the consequence is not certainty about the premise.\r\n${capRule}\r\n${emptyRule}\r\n\r\n### SCHEMA FORMAT:\r\n${multi ? MULTI_FILE_SCHEMA_FORMAT : SINGLE_FILE_SCHEMA_FORMAT}\r\n\r\nIdentify security risks such as XSS, SQLi, CSRF, insecure randomness, and data leaks that the diff actually introduces.`;\r\n}\r\n\r\nexport const fileReviewSystemPromptBase = buildFileReviewSystemPromptBase();\r\n\r\nexport function buildFileReviewSystemPrompt(\r\n config: RepoConfig['review'],\r\n languagePersona?: string,\r\n opts?: { multiFile?: boolean },\r\n) {\r\n const persona = languagePersona ? ` as ${languagePersona}` : '';\r\n const prompt = buildFileReviewSystemPromptBase(opts)\r\n .replace('{{MAX_COMMENTS}}', generatorFindingCap(config.max_comments).toString());\r\n return `You are a world-class professional senior code reviewer${persona}. ${prompt}`;\r\n}\r\n\r\nexport type RejectedExemplar = { title: string; claimType?: string | null };\r\n\r\n\r\n\r\nfunction renderExemplars(exemplars: readonly RejectedExemplar[] | undefined): string | null {\r\n if (!exemplars?.length) return null;\r\n\r\n const lines: string[] = [];\r\n let used = 0;\r\n for (const exemplar of exemplars) {\r\n const line = `- ${exemplar.title}${exemplar.claimType ? ` (${exemplar.claimType})` : ''}`;\r\n if (used + line.length > EXEMPLAR_BLOCK_CHARS) break;\r\n lines.push(line);\r\n used += line.length;\r\n }\r\n if (lines.length === 0) return null;\r\n\r\n const heading = 'Findings a reviewer on THIS repository has already rejected. Do not report things like these:';\r\n return [heading, ...lines].join('\\n');\r\n}\r\n\r\n\r\n\r\nfunction renderPrContext(prDescription: string | null): string | null {\r\n const trimmed = prDescription?.trim();\r\n if (!trimmed) return null;\r\n return `PR description (author intent - use to judge whether a change is deliberate):\\n${trimmed.slice(0, PR_DESCRIPTION_CHARS)}${trimmed.length > PR_DESCRIPTION_CHARS ? '…' : ''}`;\r\n}\r\n\r\nfunction renderCustomRules(config: RepoConfig['review']): string {\r\n const rules = config.custom_rules.length > 0 ? config.custom_rules.map((rule) => `- ${rule}`).join('\\n') : '- None';\r\n return `Custom rules:\\n${rules}`;\r\n}\r\n\r\nfunction renderLanguageGuidelines(path: string): string {\r\n const languageInfo = getLanguageForFile(path);\r\n const guidelineHeader = 'Specific Guidelines (check the diff against each of these)';\r\n return languageInfo\r\n ? `Language: ${languageInfo.language}\\n${guidelineHeader}:\\n${languageInfo.guidelines.map(g => `- ${g}`).join('\\n')}`\r\n : 'Language: Generic\\nSpecific Guidelines: Follow general best practices.';\r\n}\r\n\r\nexport function buildFileReviewPrompts(input: {\r\n file: FileDiff;\r\n prTitle: string | null;\r\n prDescription: string | null;\r\n config: RepoConfig['review'];\r\n rejectedExemplars?: readonly RejectedExemplar[];\r\n}) {\r\n const languageInfo = getLanguageForFile(input.file.path);\r\n const rules = input.config.custom_rules.length > 0 ? input.config.custom_rules.map((rule) => `- ${rule}`).join('\\n') : '- None';\r\n const systemPrompt = buildFileReviewSystemPrompt(input.config, languageInfo?.persona);\r\n const languageGuidelines = renderLanguageGuidelines(input.file.path);\r\n\r\n const prContext = renderPrContext(input.prDescription);\r\n\r\n const exemplars = renderExemplars(input.rejectedExemplars);\r\n\r\n const userPrompt = [\r\n `PR title: ${input.prTitle ?? 'Untitled PR'}`,\r\n ...(prContext ? [prContext] : []),\r\n ...(exemplars ? [exemplars] : []),\r\n `File path: ${input.file.path}`,\r\n languageGuidelines,\r\n `Custom rules:\\n${rules}`,\r\n 'Review ONLY the diff shown below. You cannot see the rest of the file or repository - do not report something as undefined, unimported, unused, or missing just because it is not in the diff. If the diff note says it was truncated, do not infer issues from omitted lines.',\r\n 'Line numbers: every diff line below is prefixed with two columns - the OLD file line number, then the NEW file line number. Always report `line` (and `line_range`) using the NEW (second, right-hand) number, and only ever cite a line that appears in the diff. For a removed line, cite the nearest NEW line number shown next to it.',\r\n 'Evidence: every finding must carry an `evidence` string containing the exact code of the line it is about, copied character-for-character from the diff below. Strip the two leading line-number columns and the +/-/space marker - quote only the code itself. A finding whose evidence does not appear in the diff will be discarded.',\r\n 'Prioritize correctness, security, and production-impacting bugs. Raise anything you can ground in a quoted line; avoid subjective style feedback.',\r\n '',\r\n `## Output JSON Schema (STRICTLY REQUIRED)`,\r\n `{\r\n \"findings\": [\r\n {\r\n \"evidence\": \"<exact line of code copied verbatim from the diff below, without the line-number columns or +/- marker>\",\r\n \"code_location\": {\r\n \"absolute_file_path\": \"${input.file.path}\",\r\n \"line\": <int>,\r\n \"line_range\": {\"start\": <int>, \"end\": <int>}\r\n },\r\n \"claim_type\": \"<${claimTypes.join(' | ')}>\",\r\n \"title\": \"<Plain title>\",\r\n \"body\": \"<Technical explanation>\",\r\n \"priority\": <0|1|2|3|4>,\r\n \"code_suggestion\": \"string\"\r\n }\r\n ],\r\n \"overall_correctness\": \"patch is correct\" | \"patch is incorrect\",\r\n \"overall_explanation\": \"Summary\",\r\n \"overall_confidence_score\": <float 0.0-1.0>\r\n}`,\r\n '',\r\n 'Unified diff:',\r\n renderFileDiff(input.file),\r\n ].join('\\n');\r\n\r\n return { systemPrompt, userPrompt };\r\n}\r\n\r\nfunction packFileHeader(file: FileDiff, index: number, total: number): string {\r\n return `===== FILE ${index + 1} of ${total}: ${file.path} =====`;\r\n}\r\n\r\nexport function buildBatchReviewPrompts(input: {\r\n files: readonly FileDiff[];\r\n prTitle: string | null;\r\n prDescription: string | null;\r\n config: RepoConfig['review'];\r\n rejectedExemplars?: readonly RejectedExemplar[];\r\n}) {\r\n const files = input.files;\r\n\r\n const languages = new Set(files.map((file) => getLanguageForFile(file.path)));\r\n const uniformLanguage = languages.size === 1 ? [...languages][0] : undefined;\r\n\r\n const systemPrompt = buildFileReviewSystemPrompt(input.config, uniformLanguage?.persona, { multiFile: true });\r\n\r\n const prContext = renderPrContext(input.prDescription);\r\n const exemplars = renderExemplars(input.rejectedExemplars);\r\n const pathList = files.map((file) => `- ${file.path}`).join('\\n');\r\n\r\n const fileBlocks = files.flatMap((file, index) => [\r\n '',\r\n packFileHeader(file, index, files.length),\r\n ...(uniformLanguage ? [] : [renderLanguageGuidelines(file.path)]),\r\n 'Unified diff:',\r\n renderFileDiff(file),\r\n ]);\r\n\r\n const userPrompt = [\r\n `PR title: ${input.prTitle ?? 'Untitled PR'}`,\r\n ...(prContext ? [prContext] : []),\r\n ...(exemplars ? [exemplars] : []),\r\n `You are reviewing ${files.length} files in ONE response. Return exactly ${files.length} entries in \"files\", one per path, in this order:\\n${pathList}`,\r\n ...(uniformLanguage ? [renderLanguageGuidelines(files[0].path)] : []),\r\n renderCustomRules(input.config),\r\n 'Review ONLY the diffs shown below. You cannot see the rest of any file or the repository - do not report something as undefined, unimported, unused, or missing just because it is not in a diff. If a diff note says it was truncated, do not infer issues from omitted lines.',\r\n 'File scoping: each finding belongs to exactly ONE file. Put it inside that file\\'s entry, set that file\\'s path in `absolute_file_path`, and quote evidence from that file\\'s diff only. Never report a finding about one file inside another file\\'s entry, and never quote a line from a different file.',\r\n 'Line numbers: every diff line below is prefixed with two columns - the OLD file line number, then the NEW file line number. Always report `line` (and `line_range`) using the NEW (second, right-hand) number, and only ever cite a line that appears in that file\\'s diff. For a removed line, cite the nearest NEW line number shown next to it.',\r\n 'Evidence: every finding must carry an `evidence` string containing the exact code of the line it is about, copied character-for-character from its own file\\'s diff below. Strip the two leading line-number columns and the +/-/space marker - quote only the code itself. A finding whose evidence does not appear in that file\\'s diff will be discarded.',\r\n 'Prioritize correctness, security, and production-impacting bugs. Raise anything you can ground in a quoted line; avoid subjective style feedback.',\r\n '',\r\n `## Output JSON Schema (STRICTLY REQUIRED)`,\r\n MULTI_FILE_SCHEMA_FORMAT,\r\n ...fileBlocks,\r\n ].join('\\n');\r\n\r\n return { systemPrompt, userPrompt };\r\n}\r\n\r\nexport function renderFileDiff(file: FileDiff) {\r\n const lines = [`diff --git a/${file.previousPath ?? file.path} b/${file.path}`];\r\n for (const hunk of file.hunks) {\r\n lines.push(hunk.header);\r\n for (const line of hunk.lines) {\r\n const prefix = line.kind === 'add' ? '+' : line.kind === 'del' ? '-' : ' ';\r\n const left = line.oldLineNumber ?? '';\r\n const right = line.newLineNumber ?? '';\r\n lines.push(`${String(left).padStart(4, ' ')} ${String(right).padStart(4, ' ')} ${prefix}${line.content}`);\r\n }\r\n }\r\n\r\n if (file.isTruncated) {\r\n lines.push('');\r\n lines.push(`[NOTE: This diff has been truncated from ${file.originalLineCount} lines to ${file.lineCount} lines for brevity.]`);\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/fingerprint.ts"],"sourcesContent":["\r\nexport function fnv1a32Hex(input: string): string {\r\n let hash = 0x811c9dc5;\r\n for (let i = 0; i < input.length; i++) {\r\n hash ^= input.charCodeAt(i);\r\n hash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\r\n }\r\n return hash.toString(16).padStart(8, '0');\r\n}\r\n\r\nexport function normalizeDiffText(input: string): string {\r\n return input\r\n .replace(/^\\s*\\d*\\s+\\d*\\s*[+\\- ]?/, '')\r\n .replace(/\\s+/g, ' ')\r\n .trim();\r\n}\r\n\r\nexport function foldEvidenceText(input: string): string {\r\n return normalizeDiffText(input)\r\n .replace(/[‘’‚‛′]/g, \"'\")\r\n .replace(/[“”„‟″]/g, '\"')\r\n .replace(/[‐-―−]/g, '-')\r\n .replace(/…/g, '...');\r\n}\r\n\r\nexport function normalizeFindingTitle(title: string): string {\r\n return title.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();\r\n}\r\n\r\nexport function buildFindingFingerprint(path: string, title: string): string {\r\n return fnv1a32Hex(`${path}\\u0000${normalizeFindingTitle(title)}`);\r\n}\r\n\r\nexport function buildFindingFingerprintV2(\r\n path: string,\r\n claimType: string | null | undefined,\r\n anchorHash: string | null | undefined,\r\n): string | null {\r\n if (!anchorHash) return null;\r\n return fnv1a32Hex(`v2 ${path} ${claimType ?? 'other'} ${anchorHash}`);\r\n}\r\n\r\nexport function buildAnchorHash(lineContent: string): string {\r\n return fnv1a32Hex(normalizeDiffText(lineContent));\r\n}\r\n"],"mappings":";AACO,SAAS,WAAW,OAAuB;AAChD,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAQ,MAAM,WAAW,CAAC;AAC1B,WAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ,SAAU;AAAA,EAC7F;AACA,SAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1C;AAEO,SAAS,kBAAkB,OAAuB;AACvD,SAAO,MACJ,QAAQ,2BAA2B,EAAE,EACrC,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEO,SAAS,iBAAiB,OAAuB;AACtD,SAAO,kBAAkB,KAAK,EAC3B,QAAQ,YAAY,GAAG,EACvB,QAAQ,YAAY,GAAG,EACvB,QAAQ,WAAW,GAAG,EACtB,QAAQ,MAAM,KAAK;AACxB;AAEO,SAAS,sBAAsB,OAAuB;AAC3D,SAAO,MAAM,YAAY,EAAE,QAAQ,eAAe,GAAG,EAAE,KAAK;AAC9D;AAEO,SAAS,wBAAwB,MAAc,OAAuB;AAC3E,SAAO,WAAW,GAAG,IAAI,KAAS,sBAAsB,KAAK,CAAC,EAAE;AAClE;AAEO,SAAS,0BACd,MACA,WACA,YACe;AACf,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,WAAW,MAAM,IAAI,IAAI,aAAa,OAAO,IAAI,UAAU,EAAE;AACtE;AAEO,SAAS,gBAAgB,aAA6B;AAC3D,SAAO,WAAW,kBAAkB,WAAW,CAAC;AAClD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/diff/index.ts","../src/diff/position.ts"],"sourcesContent":["import picomatch from 'picomatch';\r\nimport type { RepoConfig } from '@codraoss/schema';\r\nimport {\r\n type DiffLineKind,\r\n type DiffLine,\r\n type DiffHunk,\r\n type FileDiff,\r\n getValidNewLines,\r\n getValidPositions,\r\n findPositionForLine,\r\n truncateFileDiff,\r\n chunkFileDiff,\r\n} from './position';\r\n\r\nexport {\r\n type DiffLineKind,\r\n type DiffLine,\r\n type DiffHunk,\r\n type FileDiff,\r\n getValidNewLines,\r\n getValidPositions,\r\n findPositionForLine,\r\n truncateFileDiff,\r\n chunkFileDiff,\r\n};\r\n\r\nconst defaultSkipMatchers = ['**/*.lock', '**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock', '**/*.min.js'].map((pattern) =>\r\n picomatch(pattern, { dot: true }),\r\n);\r\n\r\nexport function isReviewableFile(path: string, customMatchers: ReturnType<typeof picomatch>[]) {\r\n if (defaultSkipMatchers.some((matcher) => matcher(path))) return false;\r\n if (customMatchers.some((matcher) => matcher(path))) return false;\r\n return true;\r\n}\r\n\r\nexport function parseDiffHeaderPath(line: string) {\r\n const rest = line.slice('diff --git '.length);\r\n\r\n if (rest.startsWith('a/')) {\r\n const n = (rest.length - 5) / 2;\r\n if (Number.isInteger(n) && n > 0 && rest[2 + n] === ' ' && rest.startsWith('b/', 3 + n)) {\r\n const a = rest.slice(2, 2 + n);\r\n if (a === rest.slice(5 + n)) return a;\r\n }\r\n }\r\n\r\n const bStart = rest.indexOf(' b/', rest.startsWith('a/') ? 2 : 0);\r\n const bPath = bStart === -1 ? rest.slice(rest.lastIndexOf(' ') + 1) : rest.slice(bStart + 3);\r\n return bPath.startsWith('b/') ? bPath.slice(2) : bPath;\r\n}\r\n\r\nfunction parseHunkHeader(line: string): { oldLine: number; newLine: number } | null {\r\n const match = /^@@ -(\\d+)(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/.exec(line);\r\n if (!match) {\r\n return null;\r\n }\r\n\r\n return {\r\n oldLine: Number.parseInt(match[1], 10),\r\n newLine: Number.parseInt(match[2], 10),\r\n };\r\n}\r\n\r\nfunction classifyDiffLine(prefix: ' ' | '+' | '-', content: string, oldLine: number, newLine: number, position: number): DiffLine {\r\n if (prefix === ' ') {\r\n return { kind: 'context', content, oldLineNumber: oldLine, newLineNumber: newLine, position };\r\n }\r\n\r\n if (prefix === '+') {\r\n return { kind: 'add', content, newLineNumber: newLine, position };\r\n }\r\n\r\n return { kind: 'del', content, oldLineNumber: oldLine, position };\r\n}\r\n\r\nfunction finishFile(files: FileDiff[], currentFile: FileDiff | null) {\r\n if (currentFile) {\r\n files.push(currentFile);\r\n }\r\n}\r\n\r\nexport function parseUnifiedDiff(rawDiff: string, reviewConfig?: RepoConfig['review']): FileDiff[] {\r\n const files: FileDiff[] = [];\r\n const customMatchers = reviewConfig?.skip_files?.map((pattern) => picomatch(pattern, { dot: true })) ?? [];\r\n\r\n let currentFile: FileDiff | null = null;\r\n let currentHunk: DiffHunk | null = null;\r\n let oldLine = 0;\r\n let newLine = 0;\r\n let position = 0;\r\n let isIgnored = false;\r\n\r\n const pushCurrentFile = () => {\r\n finishFile(files, currentFile);\r\n currentFile = null;\r\n currentHunk = null;\r\n oldLine = 0;\r\n newLine = 0;\r\n position = 0;\r\n isIgnored = false;\r\n };\r\n\r\n let startIndex = 0;\r\n const length = rawDiff.length;\r\n\r\n while (startIndex < length) {\r\n let endIndex = rawDiff.indexOf('\\n', startIndex);\r\n if (endIndex === -1) {\r\n endIndex = length;\r\n }\r\n\r\n let line = rawDiff.substring(startIndex, endIndex);\r\n if (line.charCodeAt(line.length - 1) === 13) {\r\n line = line.slice(0, -1);\r\n }\r\n\r\n startIndex = endIndex + 1;\r\n\r\n if (line.startsWith('diff --git ')) {\r\n pushCurrentFile();\r\n const path = parseDiffHeaderPath(line);\r\n\r\n currentFile = {\r\n path,\r\n previousPath: null,\r\n isNew: false,\r\n isDeleted: false,\r\n isBinary: false,\r\n lineCount: 0,\r\n hunks: [],\r\n };\r\n\r\n if (reviewConfig) {\r\n isIgnored = !isReviewableFile(path, customMatchers);\r\n }\r\n continue;\r\n }\r\n\r\n if (!currentFile) {\r\n continue;\r\n }\r\n\r\n if (line.startsWith('rename from ')) {\r\n currentFile.previousPath = line.slice(12);\r\n continue;\r\n }\r\n\r\n if (line.startsWith('rename to ')) {\r\n const nextPath = line.slice(10);\r\n currentFile.path = nextPath.startsWith('b/') ? nextPath.slice(2) : nextPath;\r\n if (reviewConfig) {\r\n isIgnored = !isReviewableFile(currentFile.path, customMatchers);\r\n }\r\n continue;\r\n }\r\n\r\n if (line.startsWith('new file mode ')) {\r\n currentFile.isNew = true;\r\n continue;\r\n }\r\n\r\n if (line.startsWith('deleted file mode ')) {\r\n currentFile.isDeleted = true;\r\n isIgnored = true;\r\n continue;\r\n }\r\n\r\n if (line.startsWith('Binary files ') || line.startsWith('GIT binary patch')) {\r\n currentFile.isBinary = true;\r\n isIgnored = true;\r\n continue;\r\n }\r\n\r\n if (line.startsWith('+++ ')) {\r\n const nextPath = line.slice(4);\r\n currentFile.path = nextPath.startsWith('b/') ? nextPath.slice(2) : nextPath;\r\n if (reviewConfig) {\r\n isIgnored = !isReviewableFile(currentFile.path, customMatchers);\r\n }\r\n continue;\r\n }\r\n\r\n if (isIgnored) {\r\n continue;\r\n }\r\n\r\n if (line.startsWith('--- ')) {\r\n continue;\r\n }\r\n\r\n if (line.startsWith('@@ ')) {\r\n const header = parseHunkHeader(line);\r\n if (!header) {\r\n continue;\r\n }\r\n\r\n oldLine = header.oldLine;\r\n newLine = header.newLine;\r\n currentHunk = { header: line, lines: [] };\r\n currentFile.hunks.push(currentHunk);\r\n continue;\r\n }\r\n\r\n if (!currentHunk) {\r\n continue;\r\n }\r\n\r\n const prefix = line[0];\r\n if (prefix !== ' ' && prefix !== '+' && prefix !== '-') {\r\n continue;\r\n }\r\n\r\n position += 1;\r\n const diffLine = classifyDiffLine(prefix, line.slice(1), oldLine, newLine, position);\r\n currentHunk.lines.push(diffLine);\r\n currentFile.lineCount += 1;\r\n\r\n if (diffLine.kind !== 'del') newLine += 1;\r\n if (diffLine.kind !== 'add') oldLine += 1;\r\n }\r\n\r\n pushCurrentFile();\r\n\r\n return files.filter((file) => file.path);\r\n}\r\n\r\n/** Shape returned by forge compare/files endpoints. Maps to GitHub's pulls/files JSON. */\r\nexport type DiffFileEntry = {\r\n filename: string;\r\n previous_filename?: string | null;\r\n status?: string;\r\n patch?: string | null;\r\n};\r\n\r\nexport function buildUnifiedDiffFromFiles(files: DiffFileEntry[]): string {\r\n const out: string[] = [];\r\n\r\n for (const file of files) {\r\n const newPath = file.filename;\r\n const oldPath = file.previous_filename || file.filename;\r\n const isAdded = file.status === 'added';\r\n const isRemoved = file.status === 'removed';\r\n\r\n out.push(`diff --git a/${oldPath} b/${newPath}`);\r\n if (isAdded) out.push('new file mode 100644');\r\n if (isRemoved) out.push('deleted file mode 100644');\r\n if (file.previous_filename && file.previous_filename !== newPath) {\r\n out.push(`rename from ${file.previous_filename}`);\r\n out.push(`rename to ${newPath}`);\r\n }\r\n\r\n if (!file.patch) {\r\n out.push(`Binary files a/${oldPath} and b/${newPath} differ`);\r\n continue;\r\n }\r\n\r\n out.push(isAdded ? '--- /dev/null' : `--- a/${oldPath}`);\r\n out.push(isRemoved ? '+++ /dev/null' : `+++ b/${newPath}`);\r\n out.push(file.patch);\r\n }\r\n\r\n return out.length > 0 ? `${out.join('\\n')}\\n` : '';\r\n}\r\n\r\nexport function filterReviewableFiles(\r\n files: FileDiff[],\r\n config: RepoConfig['review'],\r\n maxFiles: number,\r\n): { files: FileDiff[]; skipped: number } {\r\n const customMatchers = config.skip_files.map((pattern) => picomatch(pattern, { dot: true }));\r\n\r\n const reviewable: FileDiff[] = [];\r\n for (const file of files) {\r\n if (file.isDeleted || file.isBinary) continue;\r\n if (defaultSkipMatchers.some((matcher) => matcher(file.path))) continue;\r\n if (customMatchers.some((matcher) => matcher(file.path))) continue;\r\n reviewable.push(file);\r\n }\r\n reviewable.sort((left, right) => Number(left.isNew) - Number(right.isNew) || left.path.localeCompare(right.path));\r\n\r\n return {\r\n files: reviewable.slice(0, maxFiles),\r\n skipped: Math.max(0, reviewable.length - maxFiles),\r\n };\r\n}\r\n","export type DiffLineKind = 'context' | 'add' | 'del';\r\n\r\nexport type DiffLine = {\r\n kind: DiffLineKind;\r\n content: string;\r\n oldLineNumber?: number;\r\n newLineNumber?: number;\r\n position: number;\r\n};\r\n\r\nexport type DiffHunk = {\r\n header: string;\r\n lines: DiffLine[];\r\n};\r\n\r\nexport type FileDiff = {\r\n path: string;\r\n previousPath: string | null;\r\n isNew: boolean;\r\n isDeleted: boolean;\r\n isBinary: boolean;\r\n lineCount: number;\r\n hunks: DiffHunk[];\r\n isTruncated?: boolean;\r\n originalLineCount?: number;\r\n};\r\n\r\nexport function getValidNewLines(file: FileDiff) {\r\n const newLines = new Set<number>();\r\n for (const hunk of file.hunks) {\r\n for (const line of hunk.lines) {\r\n if (line.kind !== 'del' && line.newLineNumber !== undefined) {\r\n newLines.add(line.newLineNumber);\r\n }\r\n }\r\n }\r\n\r\n return newLines;\r\n}\r\n\r\nexport function getValidPositions(file: FileDiff) {\r\n const positions = new Set<number>();\r\n for (const hunk of file.hunks) {\r\n for (const line of hunk.lines) {\r\n if (line.kind !== 'del') {\r\n positions.add(line.position);\r\n }\r\n }\r\n }\r\n\r\n return positions;\r\n}\r\n\r\nexport function findPositionForLine(file: FileDiff, lineNumber: number) {\r\n for (const hunk of file.hunks) {\r\n for (const line of hunk.lines) {\r\n if (line.newLineNumber === lineNumber && line.kind !== 'del') {\r\n return line.position;\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n\r\nexport function truncateFileDiff(file: FileDiff, maxLines: number): FileDiff {\r\n if (file.lineCount <= maxLines) {\r\n return file;\r\n }\r\n\r\n let currentLines = 0;\r\n const keptHunks: DiffHunk[] = [];\r\n\r\n for (const hunk of file.hunks) {\r\n const remainingLines = maxLines - currentLines;\r\n if (remainingLines <= 0) {\r\n break;\r\n }\r\n\r\n if (hunk.lines.length <= remainingLines) {\r\n keptHunks.push(hunk);\r\n currentLines += hunk.lines.length;\r\n continue;\r\n }\r\n\r\n keptHunks.push({\r\n ...hunk,\r\n lines: hunk.lines.slice(0, remainingLines),\r\n });\r\n currentLines += remainingLines;\r\n break;\r\n }\r\n\r\n return {\r\n ...file,\r\n hunks: keptHunks,\r\n lineCount: currentLines,\r\n isTruncated: true,\r\n originalLineCount: file.lineCount,\r\n };\r\n}\r\n\r\nexport function chunkFileDiff(file: FileDiff, maxLinesPerChunk: number): FileDiff[] {\r\n if (file.lineCount <= maxLinesPerChunk) {\r\n return [file];\r\n }\r\n\r\n const chunks: FileDiff[] = [];\r\n let currentHunks: DiffHunk[] = [];\r\n let currentLines = 0;\r\n\r\n for (const hunk of file.hunks) {\r\n let linesRemainingInHunk = hunk.lines;\r\n\r\n while (linesRemainingInHunk.length > 0) {\r\n const roomInChunk = maxLinesPerChunk - currentLines;\r\n\r\n if (roomInChunk <= 0) {\r\n chunks.push({\r\n ...file,\r\n hunks: currentHunks,\r\n lineCount: currentLines,\r\n isTruncated: true,\r\n originalLineCount: file.lineCount,\r\n });\r\n currentHunks = [];\r\n currentLines = 0;\r\n continue;\r\n }\r\n\r\n if (linesRemainingInHunk.length <= roomInChunk) {\r\n currentHunks.push({\r\n ...hunk,\r\n lines: linesRemainingInHunk,\r\n });\r\n currentLines += linesRemainingInHunk.length;\r\n linesRemainingInHunk = [];\r\n } else {\r\n currentHunks.push({\r\n ...hunk,\r\n lines: linesRemainingInHunk.slice(0, roomInChunk),\r\n });\r\n currentLines += roomInChunk;\r\n linesRemainingInHunk = linesRemainingInHunk.slice(roomInChunk);\r\n }\r\n }\r\n }\r\n\r\n if (currentHunks.length > 0) {\r\n chunks.push({\r\n ...file,\r\n hunks: currentHunks,\r\n lineCount: currentLines,\r\n isTruncated: true,\r\n originalLineCount: file.lineCount,\r\n });\r\n }\r\n\r\n return chunks;\r\n}\r\n"],"mappings":"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","names":[]}
|