@mrkaran/hodor 0.4.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.
@@ -0,0 +1,178 @@
1
+ type Platform = "github" | "gitlab";
2
+ interface ParsedPrUrl {
3
+ owner: string;
4
+ repo: string;
5
+ prNumber: number;
6
+ host: string;
7
+ }
8
+ interface MrMetadata {
9
+ title?: string;
10
+ description?: string;
11
+ source_branch?: string;
12
+ target_branch?: string;
13
+ changes_count?: number;
14
+ labels?: Array<string | {
15
+ name?: string;
16
+ }>;
17
+ label_details?: Array<string | {
18
+ name?: string;
19
+ }>;
20
+ author?: {
21
+ username?: string;
22
+ name?: string;
23
+ };
24
+ pipeline?: {
25
+ status?: string;
26
+ web_url?: string;
27
+ };
28
+ Notes?: Array<NoteEntry>;
29
+ state?: string;
30
+ }
31
+ interface NoteEntry {
32
+ body?: string;
33
+ author?: {
34
+ username?: string;
35
+ name?: string;
36
+ };
37
+ created_at?: string;
38
+ system?: boolean;
39
+ }
40
+ interface ReviewMetrics {
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ cacheReadTokens: number;
44
+ cacheWriteTokens: number;
45
+ totalTokens: number;
46
+ cost: number;
47
+ turns: number;
48
+ toolCalls: number;
49
+ durationSeconds: number;
50
+ }
51
+ type ReviewPriority = 0 | 1 | 2 | 3;
52
+ type ReviewCorrectness = "patch is correct" | "patch is incorrect";
53
+ interface ReviewFinding {
54
+ title: string;
55
+ body: string;
56
+ priority: ReviewPriority;
57
+ code_location: {
58
+ absolute_file_path: string;
59
+ line_range: {
60
+ start: number;
61
+ end: number;
62
+ };
63
+ };
64
+ }
65
+ interface ReviewOutput {
66
+ findings: ReviewFinding[];
67
+ overall_correctness: ReviewCorrectness;
68
+ overall_explanation: string;
69
+ }
70
+ interface PostCommentResult {
71
+ success: boolean;
72
+ platform?: Platform;
73
+ prNumber?: number;
74
+ mrNumber?: number;
75
+ error?: string;
76
+ }
77
+
78
+ interface AgentProgressEvent {
79
+ type: "tool_start" | "tool_end" | "thinking" | "turn_start" | "turn_end" | "agent_start" | "agent_end" | "text_delta" | "thinking_delta" | "tool_result";
80
+ toolName?: string;
81
+ toolArgs?: string;
82
+ isError?: boolean;
83
+ turnIndex?: number;
84
+ delta?: string;
85
+ result?: string;
86
+ }
87
+ declare function detectPlatform(prUrl: string): Platform;
88
+ declare function parsePrUrl(prUrl: string): ParsedPrUrl;
89
+ declare function postReviewComment(opts: {
90
+ prUrl: string;
91
+ reviewText: string;
92
+ model?: string | null;
93
+ metricsFooter?: string | null;
94
+ headSha?: string | null;
95
+ }): Promise<PostCommentResult>;
96
+ declare function reviewPr(opts: {
97
+ prUrl?: string;
98
+ model?: string;
99
+ reasoningEffort?: string;
100
+ customPrompt?: string | null;
101
+ promptFile?: string | null;
102
+ cleanup?: boolean;
103
+ workspaceDir?: string | null;
104
+ includeMetricsFooter?: boolean;
105
+ onEvent?: (event: AgentProgressEvent) => void;
106
+ bedrockTags?: Record<string, string> | null;
107
+ localMode?: boolean;
108
+ diffAgainst?: string;
109
+ }): Promise<{
110
+ review: ReviewOutput;
111
+ metricsFooter: string | null;
112
+ headSha: string | null;
113
+ metrics: ReviewMetrics;
114
+ }>;
115
+
116
+ declare function buildPrReviewPrompt(opts: {
117
+ prUrl: string;
118
+ platform: Platform;
119
+ targetBranch?: string;
120
+ diffBaseSha?: string | null;
121
+ mrMetadata?: MrMetadata | null;
122
+ customInstructions?: string | null;
123
+ customPromptFile?: string | null;
124
+ embeddedDiff?: string | null;
125
+ previousReviewSha?: string | null;
126
+ localMode?: boolean;
127
+ }): string;
128
+
129
+ interface ParsedModel {
130
+ provider: string;
131
+ modelId: string;
132
+ }
133
+ /**
134
+ * Parse a model string like "anthropic/claude-sonnet-4-5" into { provider, modelId }.
135
+ * Handles bare names like "claude-sonnet-4-5" or "gpt-5" via auto-detection.
136
+ */
137
+ declare function parseModelString(model: string): ParsedModel;
138
+ /**
139
+ * Map reasoning effort strings to pi-ai thinking levels.
140
+ * Returns undefined for no reasoning.
141
+ */
142
+ declare function mapReasoningEffort(effort: string | undefined): "low" | "medium" | "high" | undefined;
143
+ /**
144
+ * Get API key with provider-aware selection.
145
+ *
146
+ * Priority:
147
+ * 1. LLM_API_KEY (universal override)
148
+ * 2. Provider-specific key (ANTHROPIC_API_KEY, OPENAI_API_KEY)
149
+ * 3. Fallback to any available key
150
+ *
151
+ * Returns null for bedrock (uses AWS credentials).
152
+ */
153
+ declare function getApiKey(model?: string): string | null;
154
+
155
+ declare function formatMetricsMarkdown(metrics: ReviewMetrics): string;
156
+ declare function printMetrics(metrics: ReviewMetrics, stream?: NodeJS.WritableStream): void;
157
+ /**
158
+ * Push review metrics to a Prometheus Pushgateway.
159
+ * Failures are logged as warnings and never thrown.
160
+ */
161
+ declare function pushMetrics(opts: {
162
+ pushgatewayUrl: string;
163
+ metrics: ReviewMetrics;
164
+ labels?: Record<string, string>;
165
+ }): Promise<void>;
166
+
167
+ declare function validateReviewOutput(review: ReviewOutput): ReviewOutput;
168
+
169
+ /**
170
+ * Render structured review output into clean markdown for PR/MR comments.
171
+ */
172
+
173
+ /**
174
+ * Render a ReviewOutput into clean markdown for posting as a PR/MR comment.
175
+ */
176
+ declare function renderMarkdown(review: ReviewOutput): string;
177
+
178
+ export { type AgentProgressEvent, type MrMetadata, type NoteEntry, type ParsedPrUrl, type Platform, type PostCommentResult, type ReviewCorrectness, type ReviewFinding, type ReviewMetrics, type ReviewOutput, type ReviewPriority, buildPrReviewPrompt, detectPlatform, formatMetricsMarkdown, getApiKey, mapReasoningEffort, parseModelString, parsePrUrl, postReviewComment, printMetrics, pushMetrics, renderMarkdown, reviewPr, validateReviewOutput };
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import {
2
+ buildPrReviewPrompt,
3
+ detectPlatform,
4
+ formatMetricsMarkdown,
5
+ getApiKey,
6
+ mapReasoningEffort,
7
+ parseModelString,
8
+ parsePrUrl,
9
+ postReviewComment,
10
+ printMetrics,
11
+ pushMetrics,
12
+ renderMarkdown,
13
+ reviewPr,
14
+ validateReviewOutput
15
+ } from "./chunk-QGUJENIG.js";
16
+ export {
17
+ buildPrReviewPrompt,
18
+ detectPlatform,
19
+ formatMetricsMarkdown,
20
+ getApiKey,
21
+ mapReasoningEffort,
22
+ parseModelString,
23
+ parsePrUrl,
24
+ postReviewComment,
25
+ printMetrics,
26
+ pushMetrics,
27
+ renderMarkdown,
28
+ reviewPr,
29
+ validateReviewOutput
30
+ };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@mrkaran/hodor",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.4.1",
7
+ "description": "AI-powered code review agent that finds bugs, security issues, and logic errors in pull requests",
8
+ "type": "module",
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "bin": {
12
+ "hodor": "dist/cli.js"
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsx src/cli.ts",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "tsc --noEmit"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "templates"
25
+ ],
26
+ "keywords": [
27
+ "ai",
28
+ "code-review",
29
+ "github",
30
+ "gitlab",
31
+ "pr",
32
+ "automation",
33
+ "agent",
34
+ "hodor"
35
+ ],
36
+ "author": "Karan",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/mr-karan/hodor"
41
+ },
42
+ "engines": {
43
+ "node": ">=22"
44
+ },
45
+ "dependencies": {
46
+ "@sinclair/typebox": "^0.34.48",
47
+ "@anthropic-ai/sdk": "^0.39.0",
48
+ "@mariozechner/pi-ai": "^0.62.0",
49
+ "@mariozechner/pi-coding-agent": "^0.62.0",
50
+ "chalk": "^5.4.0",
51
+ "commander": "^13.1.0",
52
+ "dotenv": "^16.4.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.0.0",
56
+ "tsup": "^8.0.0",
57
+ "tsx": "^4.0.0",
58
+ "typescript": "^5.7.0",
59
+ "vitest": "^3.0.0"
60
+ }
61
+ }
@@ -0,0 +1,125 @@
1
+ # Code Review Task
2
+
3
+ You are an automated code reviewer analyzing {pr_url}. The PR branch is checked out at the workspace.
4
+
5
+ ## Your Mission
6
+
7
+ Identify production bugs in the PR's diff only. You are in READ-ONLY mode - analyze code, do not modify files.
8
+
9
+ {mr_context_section}
10
+
11
+ {mr_notes_section}
12
+
13
+ {mr_reminder_section}
14
+
15
+ {incremental_section}
16
+
17
+ {embedded_diff_section}
18
+
19
+ {diff_fetch_instructions}
20
+
21
+ ## Tools Available
22
+
23
+ **Disable git pager to avoid interactive sessions:**
24
+ ```bash
25
+ export GIT_PAGER=cat
26
+ ```
27
+
28
+ **Available commands:**
29
+ - `{pr_diff_cmd}` - List changed files ONLY (run this FIRST, not full diff)
30
+ - `{git_diff_cmd} -- path/to/file` - See changes for ONE specific file at a time
31
+ - `read` - Read full file with context (use sparingly, only when needed)
32
+ - `grep` - Search for patterns across multiple files efficiently
33
+ - `submit_review` - Submit the final structured review when analysis is complete
34
+
35
+ ## Review Guidelines
36
+
37
+ You are acting as a reviewer for a proposed code change made by another engineer.
38
+
39
+ ### Bug Criteria (ALL must apply)
40
+
41
+ 1. It meaningfully impacts the accuracy, performance, security, or maintainability of the code.
42
+ 2. The bug is discrete and actionable (not a general issue with the codebase or combination of multiple issues).
43
+ 3. Fixing the bug does not demand a level of rigor that is not present in the rest of the codebase.
44
+ 4. The bug was introduced in this PR's diff (pre-existing bugs should not be flagged).
45
+ 5. The author of the PR would likely fix the issue if they were made aware of it.
46
+ 6. The bug does not rely on unstated assumptions about the codebase or author's intent.
47
+ 7. It is not enough to speculate that a change may disrupt another part of the codebase - you must identify the other parts of the code that are provably affected.
48
+ 8. The bug is clearly not just an intentional design choice by the author.
49
+
50
+ ### Comment Guidelines
51
+
52
+ 1. The comment should be clear about why the issue is a bug.
53
+ 2. The comment should appropriately communicate the severity of the issue. Do not claim an issue is more severe than it actually is.
54
+ 3. The comment should be brief. The body should be at most 1 paragraph. Do not introduce line breaks within natural language flow unless necessary for code fragments.
55
+ 4. The comment should not include any chunks of code longer than 3 lines. Any code chunks should be wrapped in markdown inline code tags or code blocks.
56
+ 5. The comment should clearly and explicitly communicate the scenarios, environments, or inputs necessary for the bug to arise. The comment should immediately indicate that the issue's severity depends on these factors.
57
+ 6. The comment's tone should be matter-of-fact and not accusatory or overly positive. It should read as a helpful AI assistant suggestion without sounding too much like a human reviewer.
58
+ 7. The comment should be written such that the author can immediately grasp the idea without close reading.
59
+ 8. The comment should avoid excessive flattery and comments that are not helpful to the author. Avoid phrasing like "Great job...", "Thanks for...".
60
+
61
+ ### Priority Levels
62
+
63
+ Tag each finding in the title with a priority level:
64
+ - **[P0] Critical**: Drop everything to fix. Blocking release, operations, or major usage. Only use for universal issues that do not depend on any assumptions about the inputs. Examples: Race conditions, null derefs, SQL injection, XSS, auth bypasses, data corruption.
65
+ - **[P1] High**: Urgent. Should be addressed in the next cycle. Will break in production under specific conditions. Examples: Logic errors, resource leaks, memory leaks.
66
+ - **[P2] Important**: Normal. To be fixed eventually. Performance or maintainability issues. Examples: N+1 queries, O(n²) algorithms, missing validation, incorrect error handling.
67
+ - **[P3] Low**: Nice to have. Code quality concerns. Examples: Code smells, magic numbers, overly complex logic, missing error messages.
68
+
69
+ Always include the matching numeric priority field in the `submit_review` payload: set `"priority"` to 0 for P0, 1 for P1, 2 for P2, or 3 for P3. The title tag and numeric priority must agree.
70
+
71
+ ### How Many Findings to Return
72
+
73
+ Output all findings that the original author would fix if they knew about it. If there is no finding that a person would definitely love to see and fix, prefer outputting no findings. Do not stop at the first qualifying finding. Continue until you've listed every qualifying finding.
74
+
75
+ ### Additional Guidelines
76
+
77
+ - Ignore trivial style unless it obscures meaning or violates documented standards.
78
+ - Use one comment per distinct issue (or a multi-line range if necessary).
79
+ - Always keep the line range as short as possible for interpreting the issue. Avoid ranges longer than 5–10 lines; instead, choose the most suitable subrange that pinpoints the problem.
80
+ - The code location should overlap with the diff.
81
+ - Stay on-branch: Never file bugs that only exist because the feature branch is missing commits already present on `{target_branch}`.
82
+
83
+ {review_process_section}
84
+
85
+ **Analysis Focus:**
86
+ - Check edge cases: empty inputs, null values, boundary conditions, error paths
87
+ - Think: What user input or race condition breaks this?
88
+ - Focus on the changes (+ and - lines), use full file context sparingly
89
+
90
+ ## Final Submission
91
+
92
+ When you are done, call `submit_review` exactly once with the final structured review.
93
+
94
+ ### submit_review payload
95
+
96
+ ```json
97
+ {
98
+ "findings": [
99
+ {
100
+ "title": "<≤ 80 chars, imperative, with [P0]/[P1]/[P2]/[P3] prefix>",
101
+ "body": "<valid Markdown explaining why this is a problem; max 1 paragraph>",
102
+ "priority": 0 | 1 | 2 | 3,
103
+ "code_location": {
104
+ "absolute_file_path": "<absolute file path>",
105
+ "line_range": {"start": <int>, "end": <int>}
106
+ }
107
+ }
108
+ ],
109
+ "overall_correctness": "patch is correct" | "patch is incorrect",
110
+ "overall_explanation": "<1-3 sentence explanation justifying the verdict>"
111
+ }
112
+ ```
113
+
114
+ ### Critical Submission Requirements
115
+
116
+ * Call `submit_review` exactly once after analysis is complete.
117
+ * Do not print the review as normal assistant text.
118
+ * Do not wrap the payload in markdown fences when calling the tool.
119
+ * If there are no findings, submit `"findings": []`.
120
+ * Every finding must include `title`, `body`, `priority`, and `code_location`.
121
+ * Use absolute file paths (for example, `/workspace/path/to/file.py`) not relative paths.
122
+ * The title must start with a priority tag: `[P0]`, `[P1]`, `[P2]`, or `[P3]`.
123
+ * `overall_correctness` must be exactly `"patch is correct"` or `"patch is incorrect"`.
124
+
125
+ {start_instruction}