@weareikko/code-review 0.0.1 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +221 -0
  3. package/bin/code-review.js +7 -0
  4. package/dist/cli-7RGxyNy6.js +6375 -0
  5. package/dist/cli-7RGxyNy6.js.map +1 -0
  6. package/dist/cli.d.ts +56 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +2 -0
  9. package/dist/config.d.ts +205 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/diagnostics.d.ts +118 -0
  12. package/dist/diagnostics.d.ts.map +1 -0
  13. package/dist/diff-lines.d.ts +48 -0
  14. package/dist/diff-lines.d.ts.map +1 -0
  15. package/dist/errors.d.ts +69 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/fingerprints.d.ts +22 -0
  18. package/dist/fingerprints.d.ts.map +1 -0
  19. package/dist/git.d.ts +50 -0
  20. package/dist/git.d.ts.map +1 -0
  21. package/dist/github.d.ts +119 -0
  22. package/dist/github.d.ts.map +1 -0
  23. package/dist/gitlab-review.d.ts +210 -0
  24. package/dist/gitlab-review.d.ts.map +1 -0
  25. package/dist/gitlab.d.ts +98 -0
  26. package/dist/gitlab.d.ts.map +1 -0
  27. package/dist/logger.d.ts +10 -0
  28. package/dist/logger.d.ts.map +1 -0
  29. package/dist/otel.d.ts +111 -0
  30. package/dist/otel.d.ts.map +1 -0
  31. package/dist/parser.d.ts +27 -0
  32. package/dist/parser.d.ts.map +1 -0
  33. package/dist/payloads.d.ts +30 -0
  34. package/dist/payloads.d.ts.map +1 -0
  35. package/dist/platform.d.ts +71 -0
  36. package/dist/platform.d.ts.map +1 -0
  37. package/dist/platforms/github.d.ts +115 -0
  38. package/dist/platforms/github.d.ts.map +1 -0
  39. package/dist/platforms/gitlab.d.ts +26 -0
  40. package/dist/platforms/gitlab.d.ts.map +1 -0
  41. package/dist/posting.d.ts +108 -0
  42. package/dist/posting.d.ts.map +1 -0
  43. package/dist/prior-threads.d.ts +40 -0
  44. package/dist/prior-threads.d.ts.map +1 -0
  45. package/dist/product.d.ts +17 -0
  46. package/dist/product.d.ts.map +1 -0
  47. package/dist/review.d.ts +18 -0
  48. package/dist/review.d.ts.map +1 -0
  49. package/dist/review.js +2 -0
  50. package/dist/skills.d.ts +92 -0
  51. package/dist/skills.d.ts.map +1 -0
  52. package/dist/skipped-retrieval.d.ts +26 -0
  53. package/dist/skipped-retrieval.d.ts.map +1 -0
  54. package/dist/summary-carryover.d.ts +48 -0
  55. package/dist/summary-carryover.d.ts.map +1 -0
  56. package/dist/triage.d.ts +37 -0
  57. package/dist/triage.d.ts.map +1 -0
  58. package/dist/types.d.ts +95 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/verify.d.ts +74 -0
  61. package/dist/verify.d.ts.map +1 -0
  62. package/package.json +90 -2
  63. package/skills/code-review/SKILL.md +112 -0
  64. package/skills/code-review/references/javascript-typescript.md +50 -0
  65. package/skills/code-review/references/php.md +46 -0
  66. package/skills/test-integrity/SKILL.md +76 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Metadata about a single completed HTTP request, reported to `onResponse`.
3
+ * Deliberately telemetry-agnostic: the OTel bridge maps these onto HTTP
4
+ * semantic-convention span attributes, but the client itself has no OTel
5
+ * dependency. Carries no secrets — the token lives in a request header, not
6
+ * the URL.
7
+ */
8
+ export interface GitHubResponseInfo {
9
+ method: string;
10
+ path: string;
11
+ url: string;
12
+ status: number;
13
+ /** Parsed Content-Length header in bytes, when the response provided one. */
14
+ responseContentLength?: number;
15
+ }
16
+ export interface GitHubClientOptions {
17
+ /**
18
+ * REST API base, honoring `GITHUB_API_URL` (default `https://api.github.com`;
19
+ * GitHub Enterprise sets it to e.g. `https://ghe.example.com/api/v3`). Paths
20
+ * are appended directly, so the base must already include any `/api/v3` prefix.
21
+ */
22
+ apiUrl?: string;
23
+ token: string;
24
+ fetchImpl?: typeof fetch;
25
+ requestTimeout?: number;
26
+ /**
27
+ * Optional instrumentation callback invoked once per completed HTTP response
28
+ * (success or error status), before any error is thrown. Used to surface HTTP
29
+ * metadata to diagnostics/OTel without coupling the client to those layers.
30
+ */
31
+ onResponse?: (info: GitHubResponseInfo) => void;
32
+ }
33
+ export declare const DEFAULT_GITHUB_API_URL = "https://api.github.com";
34
+ /**
35
+ * Extract the `rel="next"` URL from a GitHub `Link` response header, or `null`
36
+ * when there is no next page. GitHub paginates with absolute URLs in this header
37
+ * rather than the page-number headers GitLab uses.
38
+ */
39
+ export declare function parseNextLink(header: string | null | undefined): string | null;
40
+ export interface PullRequestRef {
41
+ ref: string;
42
+ sha: string;
43
+ }
44
+ export interface PullRequest {
45
+ head: PullRequestRef;
46
+ base: PullRequestRef;
47
+ /** PR title — the one-line declared intent of the change. May be empty. */
48
+ title?: string;
49
+ /** PR body — the author's full reasoning / decision log. May be empty or null. */
50
+ body?: string | null;
51
+ }
52
+ export interface GitHubUser {
53
+ id: number;
54
+ login: string;
55
+ }
56
+ /** An inline pull-request review comment (positioned against the diff). */
57
+ export interface PullRequestReviewComment {
58
+ id: number;
59
+ body?: string | null;
60
+ path?: string;
61
+ /** Line in the diff's new file, when the comment still anchors to it. */
62
+ line?: number | null;
63
+ original_line?: number | null;
64
+ side?: string | null;
65
+ in_reply_to_id?: number;
66
+ user?: GitHubUser | null;
67
+ }
68
+ /** A non-positional issue/PR-level comment (the summary note lives here). */
69
+ export interface IssueComment {
70
+ id: number;
71
+ body?: string | null;
72
+ user?: GitHubUser | null;
73
+ }
74
+ /** A single inline comment in a batched review payload. */
75
+ export interface ReviewCommentInput {
76
+ path: string;
77
+ body: string;
78
+ line?: number;
79
+ side?: string;
80
+ start_line?: number;
81
+ start_side?: string;
82
+ }
83
+ /**
84
+ * Payload for a batched pull-request review. Posted as one atomic review
85
+ * (`event: 'COMMENT'`) to avoid per-comment secondary rate limits.
86
+ */
87
+ export interface CreateReviewInput {
88
+ commit_id: string;
89
+ event?: string;
90
+ body?: string;
91
+ comments?: ReviewCommentInput[];
92
+ }
93
+ export interface Review {
94
+ id: number;
95
+ }
96
+ export declare class GitHubClient {
97
+ private readonly base;
98
+ private readonly token;
99
+ private readonly fetchImpl;
100
+ private readonly requestTimeout;
101
+ private readonly onResponse?;
102
+ constructor(options: GitHubClientOptions);
103
+ private reportResponse;
104
+ url(path: string, query?: Record<string, string | number | boolean | undefined>): string;
105
+ private headers;
106
+ private fetchWithTimeout;
107
+ private failure;
108
+ request<T>(path: string, init?: RequestInit, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
109
+ /** Follow GitHub `Link` `rel="next"` headers, accumulating every page. */
110
+ paginate<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T[]>;
111
+ getPullRequest(owner: string, repo: string, pull: number): Promise<PullRequest>;
112
+ listReviewComments(owner: string, repo: string, pull: number): Promise<PullRequestReviewComment[]>;
113
+ listIssueComments(owner: string, repo: string, pull: number): Promise<IssueComment[]>;
114
+ createReview(owner: string, repo: string, pull: number, payload: CreateReviewInput): Promise<Review>;
115
+ createIssueComment(owner: string, repo: string, pull: number, body: string): Promise<IssueComment>;
116
+ updateIssueComment(owner: string, repo: string, commentId: number, body: string): Promise<IssueComment>;
117
+ getCurrentUser(): Promise<GitHubUser>;
118
+ }
119
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ import type { AgentEvent, AgentTool } from '@earendil-works/pi-agent-core';
2
+ import type { AssistantMessage, Model } from '@earendil-works/pi-ai';
3
+ import type { Config } from './config.js';
4
+ import type { Logger } from './logger.js';
5
+ import type { PriorThread } from './prior-threads.js';
6
+ import type { Skill } from './skills.js';
7
+ import { type SkippedDiffFile } from './skipped-retrieval.js';
8
+ import { type ReviewAngle } from './triage.js';
9
+ import type { GitLabReviewSeverity, SizeSkippedFile, ThinkingLevel } from './types.js';
10
+ export interface UsageBreakdown {
11
+ input: number;
12
+ output: number;
13
+ cacheRead: number;
14
+ cacheWrite: number;
15
+ total: number;
16
+ }
17
+ /**
18
+ * MR size signals derived from the reviewed diff: files dropped for exceeding
19
+ * the char budget, and an optional "this MR is too big — decompose it" hint when
20
+ * the reviewed changed-line count crosses the configured threshold.
21
+ */
22
+ export interface ReviewSizeNotice {
23
+ sizeSkippedFiles: SizeSkippedFile[];
24
+ decomposeHint?: {
25
+ lines: number;
26
+ threshold: number;
27
+ };
28
+ /**
29
+ * Diff coverage when files were dropped for the char budget: how many changed
30
+ * lines were actually reviewed vs the total. Present only when something was
31
+ * size-skipped, so a partial review reports its coverage instead of reading as
32
+ * a confident full review.
33
+ */
34
+ coverage?: {
35
+ reviewedLines: number;
36
+ totalLines: number;
37
+ };
38
+ }
39
+ /** Token and cost usage attributed to a single pool member. */
40
+ export interface ModelUsage {
41
+ model: string;
42
+ tokens: UsageBreakdown;
43
+ cost: UsageBreakdown;
44
+ }
45
+ export interface ReviewUsage {
46
+ model: string;
47
+ /** Reasoning effort the main agent ran at, surfaced alongside the model in the footer. */
48
+ thinkingLevel: ThinkingLevel;
49
+ tokens: UsageBreakdown;
50
+ cost: UsageBreakdown;
51
+ /**
52
+ * Per-pool-member usage breakdown for heterogeneous `full`-depth runs. Each
53
+ * entry attributes the tokens/cost of the agents that ran on that pool member.
54
+ * The top-level `model`/`tokens`/`cost` stay the main model and the totals (the
55
+ * sum of all entries). Present only when more than one distinct model ran; the
56
+ * single-model path leaves it undefined so output is byte-identical to before.
57
+ */
58
+ byModel?: ModelUsage[];
59
+ skills: string[];
60
+ /**
61
+ * Size signals for surfacing in the MR summary. `sizeSkippedFiles` lists files
62
+ * dropped for the char budget; `decomposeHint` is set when the reviewed diff is
63
+ * past the configured line threshold. Both feed the prominent summary callout.
64
+ */
65
+ sizeNotice: ReviewSizeNotice;
66
+ }
67
+ export interface AgentLike {
68
+ subscribe(listener: (event: AgentEvent) => void | Promise<void>): () => void;
69
+ prompt(input: string): Promise<void>;
70
+ }
71
+ export interface CreateAgentParams {
72
+ systemPrompt: string;
73
+ model: Model<string>;
74
+ tools: AgentTool[];
75
+ thinkingLevel: ThinkingLevel;
76
+ getApiKey: () => Promise<string>;
77
+ }
78
+ export type CreateAgent = (params: CreateAgentParams) => AgentLike;
79
+ export interface RunReviewOptions {
80
+ cwd?: string;
81
+ diff: string;
82
+ /**
83
+ * Commit messages for all non-merge commits in the MR (merge-base…HEAD),
84
+ * in chronological order. When provided, a `<commits>` section is prepended
85
+ * to the user prompt so the reviewer understands the intent behind each change.
86
+ * Produced by `getMergeCommitLog` in `src/git.ts`.
87
+ */
88
+ commitLog?: string;
89
+ createAgent?: CreateAgent;
90
+ timeoutMs?: number;
91
+ logger?: Logger;
92
+ /**
93
+ * Prior developer replies to bot-posted review threads on the MR.
94
+ * When provided, a `<prior_review_feedback>` section is appended to the user
95
+ * prompt after `<diff>` so the reviewer can avoid re-raising already-acknowledged
96
+ * concerns and can provide contextual follow-up.
97
+ * Produced by `extractPriorThreads` in `src/prior-threads.ts`.
98
+ */
99
+ priorThreads?: PriorThread[];
100
+ /**
101
+ * Author-declared intent for the MR (title + description). When present and
102
+ * non-empty, an `<intent>` block is prepended to the user prompt so the
103
+ * reviewer can check the diff against the stated purpose and flag code/intent
104
+ * mismatches. A missing or empty description degrades gracefully (no block).
105
+ * Sourced from the GitLab MR via `getMergeRequest` in `src/gitlab.ts`.
106
+ */
107
+ intent?: ReviewIntent;
108
+ /**
109
+ * Called with the agent after it is created, before the first prompt.
110
+ * Use this to attach telemetry (e.g. `otelBridge.createAgentTelemetry(runId)`).
111
+ * The returned function, if any, is called after the review completes.
112
+ */
113
+ attachTelemetry?: (agent: AgentLike) => (() => void) | undefined;
114
+ }
115
+ interface ContextFile {
116
+ path: string;
117
+ content: string;
118
+ }
119
+ interface ReviewContext {
120
+ conventions: ContextFile[];
121
+ reviewRules: ContextFile[];
122
+ skills: Skill[];
123
+ }
124
+ export interface LoadReviewContextOptions {
125
+ /** Re-clone `git:` / `git+ssh:` skills, bypassing the on-disk clone cache. */
126
+ refreshGitSkills?: boolean;
127
+ }
128
+ export declare function loadReviewContext(cwd: string, skillNames?: string[], warn?: (msg: string) => void, options?: LoadReviewContextOptions): Promise<ReviewContext>;
129
+ export interface FilteredDiff {
130
+ diff: string;
131
+ /** Files dropped because they matched a noise pattern (lockfiles, generated, build output). */
132
+ noiseSkippedFiles: string[];
133
+ /** Files dropped because including them would exceed the char budget, with their diff size. */
134
+ sizeSkippedFiles: SizeSkippedFile[];
135
+ /** Number of added/removed lines in the reviewed (included) diff. */
136
+ reviewedChangedLines: number;
137
+ /** Number of added/removed lines in files dropped for the char budget. */
138
+ skippedChangedLines: number;
139
+ /**
140
+ * The raw diff text of each size-dropped file, keyed by path. Lets the caller
141
+ * write them to disk so an agentic reviewer can read the dropped diffs on
142
+ * demand instead of losing them entirely (opt-in retrieval).
143
+ */
144
+ sizeSkippedSections: Array<{
145
+ path: string;
146
+ section: string;
147
+ }>;
148
+ }
149
+ export declare function filterDiff(raw: string, maxChars?: number): FilteredDiff;
150
+ export declare function buildJSONSystemPrompt(context: ReviewContext, minSeverity: GitLabReviewSeverity): string;
151
+ /**
152
+ * Author-declared intent for the change, sourced from the GitLab MR.
153
+ * Both fields are optional and may be empty/whitespace — the renderer degrades
154
+ * gracefully and emits no intent block when there is nothing meaningful to show.
155
+ */
156
+ export interface ReviewIntent {
157
+ title?: string;
158
+ description?: string | null;
159
+ }
160
+ /**
161
+ * Build a Find system prompt specialised to one review angle. Used by `full`
162
+ * depth, which runs one finder per angle. The base prompt (severity/confidence
163
+ * tiers, output format, skills, conventions) is unchanged; an `<review_angle>`
164
+ * section narrows the finder to its lane so the finders cover breadth rather
165
+ * than all re-finding the same top issue.
166
+ */
167
+ export declare function buildAngleSystemPrompt(context: ReviewContext, minSeverity: GitLabReviewSeverity, angle: ReviewAngle): string;
168
+ export declare function buildUserPrompt(diff: string, skippedFiles?: string[], commitLog?: string, priorThreads?: PriorThread[], intent?: ReviewIntent, coverage?: {
169
+ reviewedLines: number;
170
+ totalLines: number;
171
+ }, retrievableSkipped?: SkippedDiffFile[]): string;
172
+ export declare function extractLastAssistantText(messages: AssistantMessage[]): string;
173
+ /**
174
+ * One usable model in the review pool. `id` is the original `provider/modelId`
175
+ * string — the stable identity used for fixed angle→model mapping, cross-family
176
+ * verifier selection, and per-model usage keying. `getApiKey` resolves THIS
177
+ * member's provider key, so a key for provider X is never sent to provider Y.
178
+ */
179
+ export interface PoolMember {
180
+ id: string;
181
+ model: Model<string>;
182
+ getApiKey: () => Promise<string>;
183
+ }
184
+ /**
185
+ * Build the effective model pool from `config.model` plus `config.modelPool`,
186
+ * resolving each member's own provider key and dropping (with a warning) any
187
+ * member whose key is missing/empty. Order and duplicates from `config.modelPool`
188
+ * are preserved by first occurrence. When the pool is empty or every member is
189
+ * unusable, falls back to a single-member pool of `config.model` (already
190
+ * validated to have a key) — reproducing single-model behaviour exactly.
191
+ *
192
+ * `config.apiKey` (which honours `--api-key`) is used for any member whose id
193
+ * equals `config.model`, so an explicit override key still applies; other members
194
+ * resolve their key via the provider-aware resolver.
195
+ */
196
+ export declare function buildEffectivePool(config: Config, logger: Logger): PoolMember[];
197
+ /** Blended per-token cost (input + output) used to compare model tiers. */
198
+ export declare function blendedCost(model: Model<string>): number;
199
+ /**
200
+ * Resolve `config.verifyModel` into a dedicated Verify-stage pool member. Returns
201
+ * null when unset (Verify falls back to the pool's cross-family pick) or when the
202
+ * model/key can't be resolved (warns and falls back, so a bad value never aborts
203
+ * the run). Also warns when the verify model is a *cheaper* tier than the finder:
204
+ * the Verify stage is a precision-judgment task, and a weak verifier drops real
205
+ * findings (recall loss) — so cheap-find/strong-verify is the intended shape.
206
+ */
207
+ export declare function resolveVerifyMember(config: Config, primary: PoolMember, logger: Logger): PoolMember | null;
208
+ export declare function runReview(config: Config, options: RunReviewOptions): Promise<ReviewUsage>;
209
+ export {};
210
+ //# sourceMappingURL=gitlab-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-review.d.ts","sourceRoot":"","sources":["../src/gitlab-review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import type { GitLabAuthHeader } from './config.js';
2
+ /**
3
+ * Metadata about a single completed HTTP request, reported to `onResponse`.
4
+ * Deliberately telemetry-agnostic: the OTel bridge maps these onto HTTP
5
+ * semantic-convention span attributes, but the client itself has no OTel
6
+ * dependency. Carries no secrets — the token lives in a request header, not
7
+ * the URL.
8
+ */
9
+ export interface GitLabResponseInfo {
10
+ method: string;
11
+ path: string;
12
+ url: string;
13
+ status: number;
14
+ /** Parsed Content-Length header in bytes, when the response provided one. */
15
+ responseContentLength?: number;
16
+ }
17
+ export interface GitLabClientOptions {
18
+ gitlabUrl: string;
19
+ token: string;
20
+ authHeader?: GitLabAuthHeader;
21
+ fetchImpl?: typeof fetch;
22
+ requestTimeout?: number;
23
+ /**
24
+ * Optional instrumentation callback invoked once per completed HTTP response
25
+ * (success or error status), before any error is thrown. Used to surface HTTP
26
+ * metadata to diagnostics/OTel without coupling the client to those layers.
27
+ */
28
+ onResponse?: (info: GitLabResponseInfo) => void;
29
+ }
30
+ export interface MergeRequest {
31
+ source_branch: string;
32
+ target_branch: string;
33
+ /** MR title — the one-line declared intent of the change. May be empty. */
34
+ title?: string;
35
+ /** MR description — the author's full reasoning / decision log. May be empty or null. */
36
+ description?: string | null;
37
+ }
38
+ export interface Version {
39
+ base_commit_sha: string;
40
+ start_commit_sha: string;
41
+ head_commit_sha: string;
42
+ }
43
+ export interface DiscussionNotePosition {
44
+ old_path?: string | null;
45
+ new_path?: string | null;
46
+ old_line?: number | null;
47
+ new_line?: number | null;
48
+ }
49
+ export interface DiscussionNote {
50
+ id?: number;
51
+ body?: string | null;
52
+ system?: boolean;
53
+ resolved?: boolean;
54
+ position?: DiscussionNotePosition;
55
+ }
56
+ export interface Discussion {
57
+ notes: DiscussionNote[];
58
+ }
59
+ export interface MergeRequestNote {
60
+ id: number;
61
+ body: string;
62
+ }
63
+ export interface DraftNote {
64
+ id: number;
65
+ author_id: number;
66
+ note: string;
67
+ }
68
+ export interface CurrentUser {
69
+ id: number;
70
+ }
71
+ export declare class GitLabClient {
72
+ private readonly base;
73
+ private readonly token;
74
+ private readonly authHeader;
75
+ private readonly fetchImpl;
76
+ private readonly requestTimeout;
77
+ private readonly onResponse?;
78
+ constructor(options: GitLabClientOptions);
79
+ private reportResponse;
80
+ url(path: string, query?: Record<string, string | number | boolean | undefined>): string;
81
+ private headers;
82
+ private fetchWithTimeout;
83
+ request<T>(path: string, init?: RequestInit, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
84
+ paginate<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T[]>;
85
+ getMergeRequest(project: string, mr: string): Promise<MergeRequest>;
86
+ getLatestVersion(project: string, mr: string): Promise<Version>;
87
+ getDiscussions(project: string, mr: string): Promise<Discussion[]>;
88
+ postDiscussion(project: string, mr: string, payload: unknown): Promise<unknown>;
89
+ createMergeRequestNote(project: string, mr: string, body: string): Promise<MergeRequestNote>;
90
+ updateMergeRequestNote(project: string, mr: string, noteId: number, body: string): Promise<MergeRequestNote>;
91
+ getCurrentUser(): Promise<CurrentUser>;
92
+ listDraftNotes(project: string, mr: string): Promise<DraftNote[]>;
93
+ createDraftNote(project: string, mr: string, payload: unknown): Promise<DraftNote>;
94
+ deleteDraftNote(project: string, mr: string, id: number): Promise<void>;
95
+ bulkPublishDraftNotes(project: string, mr: string): Promise<void>;
96
+ publishDraftNote(project: string, mr: string, id: number): Promise<void>;
97
+ }
98
+ //# sourceMappingURL=gitlab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../src/gitlab.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface Logger {
3
+ debug(message: string): void;
4
+ info(message: string): void;
5
+ warn(message: string): void;
6
+ error(message: string): void;
7
+ }
8
+ export declare function createLogger(minLevel?: LogLevel): Logger;
9
+ export declare const noopLogger: Logger;
10
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAI3D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,QAAQ,GAAE,QAAiB,GAAG,MAAM,CAYhE;AAED,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAC"}
package/dist/otel.d.ts ADDED
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Optional OpenTelemetry bridge over `diagnostics_channel` and the agent
3
+ * event stream.
4
+ *
5
+ * Subscribes to every `@weareikko/code-review:*` tracing channel, opens an
6
+ * OTel span on `start`, and closes it on `asyncEnd`/`error`. The `reviewer.run`
7
+ * phase additionally carries OpenTelemetry GenAI semantic-convention
8
+ * attributes (`gen_ai.*`) and emits the standardized GenAI client metrics
9
+ * (`gen_ai.client.operation.duration`, `gen_ai.client.token.usage`,
10
+ * `gen_ai.client.cost`, `gen_ai.client.time_to_first_token`) so
11
+ * metrics-driven AI observability surfaces auto-discover the service.
12
+ *
13
+ * Per-turn and per-tool-call telemetry is captured via `createAgentTelemetry`,
14
+ * which subscribes to the agent's live event stream and emits:
15
+ * - `gen_ai.agent.turn` child spans under `invoke_agent code-review`
16
+ * - `execute_tool <name>` grandchild spans under each turn
17
+ * - Per-turn `gen_ai.client.token.usage` and `gen_ai.client.cost` metrics
18
+ * - `gen_ai.client.time_to_first_token` when streaming events fire
19
+ *
20
+ * Opt-in: set `CODE_REVIEW_OTEL=1`. Exporter selection and endpoint follow
21
+ * the standard `OTEL_*` env vars (`OTEL_EXPORTER_OTLP_ENDPOINT`,
22
+ * `OTEL_EXPORTER_OTLP_HEADERS`, …).
23
+ *
24
+ * **Content capture**: set `CODE_REVIEW_OTEL_CAPTURE_CONTENT=1` to attach
25
+ * LLM output text and tool arguments/results to spans as `gen_ai.output.messages`,
26
+ * `gen_ai.tool.call.arguments`, and `gen_ai.tool.call.result`. These attributes
27
+ * may contain code diffs and review commentary — only enable after confirming
28
+ * your observability backend's data-retention and PII policies allow it.
29
+ *
30
+ * **Grafana Cloud token scopes**: for all three signals to reach their
31
+ * respective backends, the service account token used in
32
+ * `OTEL_EXPORTER_OTLP_HEADERS` must have:
33
+ * - `Traces Publisher` — writes to Tempo (traces)
34
+ * - `Metrics Publisher` — writes to Mimir (gen_ai.* histograms)
35
+ * - `Logs Publisher` — writes to Loki (structured log records)
36
+ * A token missing any of these scopes will receive `401 Unauthorized:
37
+ * invalid scope requested` silently from the OTLP gateway. Enable OTel
38
+ * diagnostics with `OTEL_LOG_LEVEL=error` to surface export failures.
39
+ *
40
+ * The OTel SDK runtime is bundled but loaded via dynamic `import()` behind the
41
+ * env check, so disabling the bridge skips the SDK boot entirely. Library
42
+ * callers who already have configured providers in their process can inject
43
+ * their own runtime via `startOtelBridge({ runtime })` so spans and metrics
44
+ * join the host providers instead of a second `NodeSDK`.
45
+ */
46
+ import type { MeterProvider, TracerProvider } from '@opentelemetry/api';
47
+ import type { LoggerProvider } from '@opentelemetry/api-logs';
48
+ import type { AgentLike } from './gitlab-review.js';
49
+ import { type GeneratedComment } from './types.js';
50
+ export interface OtelBridge {
51
+ shutdown(): Promise<void>;
52
+ /**
53
+ * Returns a function that, when called with an agent, subscribes to its live
54
+ * event stream and emits per-turn and per-tool-call OTel spans/metrics.
55
+ * Must be called after the `reviewer.run` diagnostic span is open (i.e. from
56
+ * inside `traceDiagnosticPhase('reviewer.run', ...)`). Returns `undefined`
57
+ * when the span is not yet open or OTel is disabled.
58
+ */
59
+ createAgentTelemetry(runId: string): ((agent: AgentLike) => () => void) | undefined;
60
+ /**
61
+ * Emits one structured OTel log record per generated comment to Loki/the
62
+ * configured log backend. Each record carries `event.name`,
63
+ * `gitlab_review.comment.*` attributes, and the comment body as the log
64
+ * line. Safe to call at any point after the run phase has opened.
65
+ */
66
+ logComments(comments: GeneratedComment[], runId: string): void;
67
+ }
68
+ export interface OtelRuntime {
69
+ tracerProvider: TracerProvider;
70
+ meterProvider: MeterProvider;
71
+ loggerProvider: LoggerProvider;
72
+ shutdown(): Promise<void>;
73
+ }
74
+ export interface OtelBridgeOptions {
75
+ /**
76
+ * Pre-wired OTel runtime. When provided, the bridge uses the supplied
77
+ * providers and skips dynamic import of `@opentelemetry/sdk-node`. Library
78
+ * callers with configured `TracerProvider`/`MeterProvider` should pass their
79
+ * own providers plus a no-op `shutdown`; tests inject fakes with assertion
80
+ * hooks.
81
+ */
82
+ runtime?: OtelRuntime;
83
+ /**
84
+ * Override the env source used for the opt-in check. Defaults to
85
+ * `process.env`.
86
+ */
87
+ env?: NodeJS.ProcessEnv;
88
+ /**
89
+ * When true, attaches LLM output text and tool call arguments/results to
90
+ * spans as `gen_ai.output.messages`, `gen_ai.tool.call.arguments`, and
91
+ * `gen_ai.tool.call.result`. Defaults to `isContentCaptureEnabled(env)`.
92
+ *
93
+ * Only enable after confirming your observability backend's data-retention
94
+ * and PII policies permit storing code review content.
95
+ */
96
+ captureContent?: boolean;
97
+ }
98
+ export declare function isOtelEnabled(env?: NodeJS.ProcessEnv): boolean;
99
+ /**
100
+ * Returns true when `CODE_REVIEW_OTEL_CAPTURE_CONTENT=1` (or `true`) is set.
101
+ *
102
+ * When enabled, per-turn assistant output text is attached to turn spans as
103
+ * `gen_ai.output.messages`, and tool arguments / results are attached to
104
+ * `execute_tool` spans as `gen_ai.tool.call.arguments` / `gen_ai.tool.call.result`.
105
+ *
106
+ * These fields may contain code diffs and review commentary — only enable after
107
+ * confirming your observability backend's data-retention and PII policies.
108
+ */
109
+ export declare function isContentCaptureEnabled(env?: NodeJS.ProcessEnv): boolean;
110
+ export declare function startOtelBridge(options?: OtelBridgeOptions): Promise<OtelBridge | null>;
111
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAMV,aAAa,EAGb,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAU,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAQtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAO/D,MAAM,WAAW,UAAU;IACzB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;OAMG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACpF;;;;;OAKG;IACH,WAAW,CAAC,QAAQ,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA4DD,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAIrF;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmUjG"}
@@ -0,0 +1,27 @@
1
+ import { type ReviewComment } from './types.js';
2
+ /** Why the reviewer's JSON could not be recovered. Surfaced for diagnostics. */
3
+ export type ParseFailureReason =
4
+ /** A ```json fence was present but its contents could not be parsed or repaired. */
5
+ 'fence_unparseable'
6
+ /** An unfenced reviewer-shaped object was present but could not be parsed or repaired. */
7
+ | 'object_unparseable';
8
+ export interface ParseFailure {
9
+ reason: ParseFailureReason;
10
+ /** Whitespace-collapsed first ~200 chars of the offending block, for logs/OTel. */
11
+ preview: string;
12
+ }
13
+ export interface ParseResult {
14
+ comments: ReviewComment[];
15
+ summary: string | null;
16
+ warnings: string[];
17
+ /**
18
+ * Set when the reviewer clearly intended to emit the `{ summary, comments }`
19
+ * JSON object but it could not be parsed (even after a best-effort repair),
20
+ * and nothing usable was recovered; `null` otherwise. The CLI fails loudly on
21
+ * a non-null value rather than marking the job successful with an empty review.
22
+ */
23
+ malformed: ParseFailure | null;
24
+ }
25
+ export declare function parseReviewMarkdownWithWarnings(markdown: string): ParseResult;
26
+ export declare function parseReviewMarkdown(markdown: string): ReviewComment[];
27
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,aAAa,EAAa,MAAM,YAAY,CAAC;AAGnG,gFAAgF;AAChF,MAAM,MAAM,kBAAkB;AAC5B,oFAAoF;AAClF,mBAAmB;AACrB,0FAA0F;GACxF,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;OAKG;IACH,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;AAgVD,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmB7E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAErE"}
@@ -0,0 +1,30 @@
1
+ import { type ResolvedDiffLine } from './diff-lines.js';
2
+ import type { Confidence, DiffRefs, GeneratedComment, GitLabDiscussionPayload, ReviewComment } from './types.js';
3
+ export { type ResolvedDiffLine, resolveDiffLine } from './diff-lines.js';
4
+ /**
5
+ * Builds the visible body of an inline comment.
6
+ *
7
+ * Layout:
8
+ * <bold Conventional Comment title>
9
+ * <discussion>
10
+ *
11
+ * _Confidence: <level>._
12
+ *
13
+ * ---
14
+ *
15
+ * <commit footer>
16
+ *
17
+ * The confidence line sits between the reviewer's discussion and the
18
+ * horizontal rule so developers can see the reviewer's certainty without
19
+ * scrolling past the footer. The footer mirrors the format used in the
20
+ * MR-level summary note.
21
+ *
22
+ * Neither the confidence line nor the footer is included in the fingerprint
23
+ * hash, so comment identity (deduplication) remains stable across commits
24
+ * even when the SHA in the footer changes or the reviewer revises its
25
+ * confidence judgment.
26
+ */
27
+ export declare function buildCommentBody(body: string, commitSha: string, confidence: Confidence): string;
28
+ export declare function buildPayload(comment: ReviewComment, body: string, refs: DiffRefs, resolved?: ResolvedDiffLine | null): GitLabDiscussionPayload;
29
+ export declare function buildGeneratedComments(comments: ReviewComment[], diff: string, refs: DiffRefs, existingFingerprints: Set<string>): GeneratedComment<GitLabDiscussionPayload>[];
30
+ //# sourceMappingURL=payloads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payloads.d.ts","sourceRoot":"","sources":["../src/payloads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAmB,MAAM,iBAAiB,CAAC;AAGzE,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,KAAK,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAczE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAIhG;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,EACd,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI,GACjC,uBAAuB,CAwBzB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EAAE,EACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,EACd,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,GAChC,gBAAgB,CAAC,uBAAuB,CAAC,EAAE,CAuB7C"}