@warpmetrics/review 0.1.3 → 0.1.5

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/bin/init.js CHANGED
@@ -147,9 +147,9 @@ async function main() {
147
147
  log(' 3. Open a PR to see your first AI review');
148
148
  log(' 4. View analytics at https://app.warpmetrics.com');
149
149
  log('');
150
- log(' Optional \u2014 add this badge to your README:');
151
- log(' ![warp-review](https://img.shields.io/badge/warp--review---%25%20accepted-purple)');
152
- log(' (copy the line above into your README.md)');
150
+ log(' Optional \u2014 add a dynamic badge to your README:');
151
+ log(' Enable the badge in your project settings at https://app.warpmetrics.com');
152
+ log(' to get a live acceptance rate badge powered by your WarpMetrics data.');
153
153
  log('');
154
154
 
155
155
  rl.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warpmetrics/review",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "AI code reviewer that learns your codebase. Powered by WarpMetrics.",
6
6
  "bin": {
@@ -14,13 +14,14 @@
14
14
  "files": [
15
15
  "bin/",
16
16
  "src/",
17
+ "!src/**/*.test.js",
17
18
  "defaults/",
18
19
  "README.md",
19
20
  "LICENSE"
20
21
  ],
21
22
  "dependencies": {
22
23
  "@anthropic-ai/sdk": "latest",
23
- "@warpmetrics/warp": "latest",
24
+ "@warpmetrics/warp": "^0.0.16",
24
25
  "minimatch": "^10.0.0"
25
26
  },
26
27
  "license": "MIT",
@@ -1,180 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { getValidLines, extractSnippet, buildContext } from './context.js';
3
-
4
- describe('getValidLines', () => {
5
- it('returns empty set for null/undefined patch', () => {
6
- expect(getValidLines(null)).toEqual(new Set());
7
- expect(getValidLines(undefined)).toEqual(new Set());
8
- });
9
-
10
- it('parses a simple hunk with added and context lines', () => {
11
- const patch = [
12
- '@@ -10,4 +10,6 @@',
13
- ' const a = 1;',
14
- '+const b = 2;',
15
- '+const c = 3;',
16
- ' const d = 4;',
17
- ' const e = 5;',
18
- ].join('\n');
19
-
20
- const valid = getValidLines(patch);
21
- // Context lines: 10, 13, 14; Added lines: 11, 12
22
- expect(valid.has(10)).toBe(true);
23
- expect(valid.has(11)).toBe(true);
24
- expect(valid.has(12)).toBe(true);
25
- expect(valid.has(13)).toBe(true);
26
- expect(valid.has(14)).toBe(true);
27
- expect(valid.size).toBe(5);
28
- });
29
-
30
- it('skips deleted lines (do not count toward line numbers)', () => {
31
- const patch = [
32
- '@@ -5,4 +5,3 @@',
33
- ' keep',
34
- '-removed',
35
- ' also keep',
36
- ' end',
37
- ].join('\n');
38
-
39
- const valid = getValidLines(patch);
40
- expect(valid.has(5)).toBe(true); // keep
41
- expect(valid.has(6)).toBe(true); // also keep
42
- expect(valid.has(7)).toBe(true); // end
43
- expect(valid.size).toBe(3);
44
- });
45
-
46
- it('handles multiple hunks', () => {
47
- const patch = [
48
- '@@ -1,3 +1,3 @@',
49
- ' a',
50
- '+b',
51
- ' c',
52
- '@@ -20,3 +20,3 @@',
53
- ' x',
54
- '+y',
55
- ' z',
56
- ].join('\n');
57
-
58
- const valid = getValidLines(patch);
59
- expect(valid.has(1)).toBe(true);
60
- expect(valid.has(2)).toBe(true);
61
- expect(valid.has(3)).toBe(true);
62
- expect(valid.has(20)).toBe(true);
63
- expect(valid.has(21)).toBe(true);
64
- expect(valid.has(22)).toBe(true);
65
- });
66
-
67
- it('ignores "No newline at end of file" marker', () => {
68
- const patch = [
69
- '@@ -1,2 +1,2 @@',
70
- '-old',
71
- '+new',
72
- '\',
73
- ].join('\n');
74
-
75
- const valid = getValidLines(patch);
76
- expect(valid.has(1)).toBe(true);
77
- expect(valid.size).toBe(1);
78
- });
79
- });
80
-
81
- describe('extractSnippet', () => {
82
- const patch = [
83
- '@@ -10,5 +10,7 @@',
84
- ' line10',
85
- '+line11',
86
- '+line12',
87
- ' line13',
88
- ' line14',
89
- '+line15',
90
- ' line16',
91
- ].join('\n');
92
-
93
- it('returns null for null patch', () => {
94
- expect(extractSnippet(null, 11)).toBeNull();
95
- });
96
-
97
- it('returns null if target line not found', () => {
98
- expect(extractSnippet(patch, 999)).toBeNull();
99
- });
100
-
101
- it('extracts 3 lines of context around target', () => {
102
- const snippet = extractSnippet(patch, 12);
103
- // line before (11), target (12), line after (13)
104
- expect(snippet).toBe('line11\nline12\n line13');
105
- });
106
-
107
- it('handles target at start of patch (no line before)', () => {
108
- const snippet = extractSnippet(patch, 10);
109
- // no line before, target (10), line after (11)
110
- expect(snippet).toBe(' line10\nline11');
111
- });
112
-
113
- it('handles target at end of patch (no line after)', () => {
114
- const snippet = extractSnippet(patch, 16);
115
- // line before (15), target (16), no line after
116
- expect(snippet).toBe('line15\n line16');
117
- });
118
-
119
- it('strips + prefix from added lines', () => {
120
- const snippet = extractSnippet(patch, 11);
121
- expect(snippet).not.toContain('+');
122
- expect(snippet).toBe(' line10\nline11\nline12');
123
- });
124
- });
125
-
126
- describe('buildContext', () => {
127
- it('includes diff for all files', () => {
128
- const files = [
129
- { filename: 'a.js', status: 'modified', patch: '+added', content: 'full content' },
130
- { filename: 'b.js', status: 'added', patch: '+new file', content: 'new' },
131
- ];
132
- const { userMessage } = buildContext(files, {});
133
- expect(userMessage).toContain('## File: a.js (modified)');
134
- expect(userMessage).toContain('## File: b.js (added)');
135
- expect(userMessage).toContain('+added');
136
- expect(userMessage).toContain('+new file');
137
- });
138
-
139
- it('returns truncatedCount when full content is dropped', () => {
140
- // Create a file whose diff fits but full content exceeds budget
141
- const largePatch = 'x'.repeat(100);
142
- const largeContent = 'y'.repeat(800_000); // ~200K tokens, exceeds budget
143
- const files = [
144
- { filename: 'big.js', status: 'modified', patch: largePatch, content: largeContent },
145
- ];
146
- const { userMessage, truncatedCount } = buildContext(files, {});
147
- expect(userMessage).toContain('big.js');
148
- expect(userMessage).toContain('full content omitted');
149
- expect(truncatedCount).toBe(1);
150
- });
151
-
152
- it('returns truncatedCount 0 when everything fits', () => {
153
- const files = [
154
- { filename: 'small.js', status: 'modified', patch: '+a', content: 'const a = 1;' },
155
- ];
156
- const { truncatedCount } = buildContext(files, {});
157
- expect(truncatedCount).toBe(0);
158
- });
159
-
160
- it('handles files with no content', () => {
161
- const files = [
162
- { filename: 'a.js', status: 'modified', patch: '+line', content: null },
163
- ];
164
- const { userMessage, truncatedCount } = buildContext(files, {});
165
- expect(userMessage).toContain('+line');
166
- expect(userMessage).not.toContain('Full file content');
167
- expect(truncatedCount).toBe(0);
168
- });
169
-
170
- it('sorts by diff size ascending', () => {
171
- const files = [
172
- { filename: 'big.js', status: 'modified', patch: 'x'.repeat(1000), content: null },
173
- { filename: 'small.js', status: 'modified', patch: 'y', content: null },
174
- ];
175
- const { userMessage } = buildContext(files, {});
176
- const bigIdx = userMessage.indexOf('big.js');
177
- const smallIdx = userMessage.indexOf('small.js');
178
- expect(smallIdx).toBeLessThan(bigIdx);
179
- });
180
- });
@@ -1,24 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { buildSystemPrompt } from './prompt.js';
3
-
4
- describe('buildSystemPrompt', () => {
5
- it('injects skills, title, and body', () => {
6
- const result = buildSystemPrompt('- Flag null access', 'Fix auth bug', 'Fixes login crash');
7
- expect(result).toContain('- Flag null access');
8
- expect(result).toContain('Title: Fix auth bug');
9
- expect(result).toContain('Description: Fixes login crash');
10
- });
11
-
12
- it('includes response format with category list', () => {
13
- const result = buildSystemPrompt('skills', 'title', 'body');
14
- expect(result).toContain('`bug`');
15
- expect(result).toContain('`security`');
16
- expect(result).toContain('`performance`');
17
- expect(result).toContain('JSON array');
18
- });
19
-
20
- it('includes instruction about diff-only lines', () => {
21
- const result = buildSystemPrompt('skills', 'title', 'body');
22
- expect(result).toContain('Only comment on lines that appear in the diff');
23
- });
24
- });