@motivation-labs/crosscheck 0.14.0-beta.10 → 0.14.0-beta.20

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 (39) hide show
  1. package/dist/__tests__/comment-bodies.test.d.ts +2 -0
  2. package/dist/__tests__/comment-bodies.test.d.ts.map +1 -0
  3. package/dist/__tests__/comment-bodies.test.js +75 -0
  4. package/dist/__tests__/comment-bodies.test.js.map +1 -0
  5. package/dist/__tests__/diff-hash.test.d.ts +2 -0
  6. package/dist/__tests__/diff-hash.test.d.ts.map +1 -0
  7. package/dist/__tests__/diff-hash.test.js +126 -0
  8. package/dist/__tests__/diff-hash.test.js.map +1 -0
  9. package/dist/__tests__/is-fresh-review-comment.test.d.ts +2 -0
  10. package/dist/__tests__/is-fresh-review-comment.test.d.ts.map +1 -0
  11. package/dist/__tests__/is-fresh-review-comment.test.js +71 -0
  12. package/dist/__tests__/is-fresh-review-comment.test.js.map +1 -0
  13. package/dist/__tests__/scopes.test.d.ts +2 -0
  14. package/dist/__tests__/scopes.test.d.ts.map +1 -0
  15. package/dist/__tests__/scopes.test.js +101 -0
  16. package/dist/__tests__/scopes.test.js.map +1 -0
  17. package/dist/commands/watch.d.ts.map +1 -1
  18. package/dist/commands/watch.js +175 -30
  19. package/dist/commands/watch.js.map +1 -1
  20. package/dist/github/client.d.ts +1 -0
  21. package/dist/github/client.d.ts.map +1 -1
  22. package/dist/github/client.js +42 -16
  23. package/dist/github/client.js.map +1 -1
  24. package/dist/lib/comment-bodies.d.ts +17 -0
  25. package/dist/lib/comment-bodies.d.ts.map +1 -0
  26. package/dist/lib/comment-bodies.js +51 -0
  27. package/dist/lib/comment-bodies.js.map +1 -0
  28. package/dist/lib/diff-hash.d.ts +16 -0
  29. package/dist/lib/diff-hash.d.ts.map +1 -0
  30. package/dist/lib/diff-hash.js +71 -0
  31. package/dist/lib/diff-hash.js.map +1 -0
  32. package/dist/lib/runner.d.ts.map +1 -1
  33. package/dist/lib/runner.js +32 -0
  34. package/dist/lib/runner.js.map +1 -1
  35. package/dist/lib/scopes.d.ts +16 -0
  36. package/dist/lib/scopes.d.ts.map +1 -0
  37. package/dist/lib/scopes.js +37 -0
  38. package/dist/lib/scopes.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=comment-bodies.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-bodies.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/comment-bodies.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildFixAppliedCommentBody, buildConflictResolvedCommentBody, } from '../lib/comment-bodies.js';
3
+ describe('buildFixAppliedCommentBody', () => {
4
+ it('includes a 7-char sha link, applied count, and the fix_applied annotation tag', () => {
5
+ const body = buildFixAppliedCommentBody({
6
+ owner: 'codatta',
7
+ repo: 'humanbased-monorepo',
8
+ sha: 'abcdef0123456789',
9
+ appliedCount: 3,
10
+ reviewCommentId: 4555000111,
11
+ });
12
+ expect(body).toContain('[`abcdef0`](https://github.com/codatta/humanbased-monorepo/commit/abcdef0123456789)');
13
+ expect(body).toContain('**3 changes applied**');
14
+ expect(body).toContain('(#issuecomment-4555000111)');
15
+ expect(body).toContain('<!-- crosscheck: fix_applied -->');
16
+ });
17
+ it('pluralizes change/changes correctly for 1 vs N', () => {
18
+ const one = buildFixAppliedCommentBody({
19
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), appliedCount: 1, reviewCommentId: 1,
20
+ });
21
+ const many = buildFixAppliedCommentBody({
22
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), appliedCount: 5, reviewCommentId: 1,
23
+ });
24
+ expect(one).toContain('**1 change applied**');
25
+ expect(many).toContain('**5 changes applied**');
26
+ });
27
+ it('omits the review backlink when no commentId is provided', () => {
28
+ const body = buildFixAppliedCommentBody({
29
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), appliedCount: 2,
30
+ });
31
+ expect(body).not.toContain('issuecomment-');
32
+ expect(body).not.toContain('addressing the');
33
+ });
34
+ });
35
+ describe('buildConflictResolvedCommentBody', () => {
36
+ it('lists each resolved file as a code-fenced bullet and includes the annotation tag', () => {
37
+ const body = buildConflictResolvedCommentBody({
38
+ owner: 'codatta',
39
+ repo: 'humanbased-monorepo',
40
+ sha: '0123456789abcdef',
41
+ conflictCount: 2,
42
+ files: ['src/a.ts', 'src/b.ts'],
43
+ });
44
+ expect(body).toContain('Resolved 2 conflicts in:');
45
+ expect(body).toContain('- `src/a.ts`');
46
+ expect(body).toContain('- `src/b.ts`');
47
+ expect(body).toContain('[`0123456`]');
48
+ expect(body).toContain('<!-- crosscheck: conflict_resolved -->');
49
+ });
50
+ it('pluralizes conflict/conflicts correctly', () => {
51
+ const one = buildConflictResolvedCommentBody({
52
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), conflictCount: 1, files: ['x.ts'],
53
+ });
54
+ expect(one).toContain('Resolved 1 conflict in:');
55
+ });
56
+ it('truncates the file list at 20 entries and shows a "...and N more" tail', () => {
57
+ const files = Array.from({ length: 25 }, (_, i) => `src/file-${i}.ts`);
58
+ const body = buildConflictResolvedCommentBody({
59
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), conflictCount: 25, files,
60
+ });
61
+ expect(body).toContain('- `src/file-0.ts`');
62
+ expect(body).toContain('- `src/file-19.ts`');
63
+ expect(body).not.toContain('- `src/file-20.ts`');
64
+ expect(body).toContain('- _...and 5 more_');
65
+ });
66
+ it('omits the truncation tail when the file list fits', () => {
67
+ const body = buildConflictResolvedCommentBody({
68
+ owner: 'o', repo: 'r', sha: 'a'.repeat(40), conflictCount: 3,
69
+ files: ['a.ts', 'b.ts', 'c.ts'],
70
+ });
71
+ expect(body).not.toContain('...and');
72
+ expect(body).not.toContain('more_');
73
+ });
74
+ });
75
+ //# sourceMappingURL=comment-bodies.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-bodies.test.js","sourceRoot":"","sources":["../../src/__tests__/comment-bodies.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diff-hash.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-hash.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/diff-hash.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { execSync } from 'child_process';
3
+ import { mkdtempSync, rmSync, writeFileSync, readFileSync, existsSync } from 'fs';
4
+ import { tmpdir } from 'os';
5
+ import { join } from 'path';
6
+ import { computeDiffHash, PersistentDiffHashMap } from '../lib/diff-hash.js';
7
+ function git(args, cwd) {
8
+ return execSync(`git ${args}`, { cwd, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
9
+ }
10
+ function setupRepo() {
11
+ const dir = mkdtempSync(join(tmpdir(), 'crosscheck-diff-test-'));
12
+ git('init --quiet -b main', dir);
13
+ git('config user.email test@test.local', dir);
14
+ git('config user.name Test', dir);
15
+ writeFileSync(join(dir, 'file.txt'), 'base content\n');
16
+ git('add .', dir);
17
+ git('commit --quiet -m base', dir);
18
+ // Create the "origin/main" remote-tracking ref that computeDiffHash expects
19
+ git('update-ref refs/remotes/origin/main HEAD', dir);
20
+ return { dir, baseRef: 'main' };
21
+ }
22
+ describe('computeDiffHash', () => {
23
+ let dir;
24
+ let baseRef;
25
+ beforeEach(() => {
26
+ ;
27
+ ({ dir, baseRef } = setupRepo());
28
+ });
29
+ afterEach(() => {
30
+ rmSync(dir, { recursive: true, force: true });
31
+ });
32
+ it('returns a stable hex digest for a given patch', () => {
33
+ writeFileSync(join(dir, 'file.txt'), 'modified content\n');
34
+ git('commit --quiet -am edit', dir);
35
+ const h1 = computeDiffHash(dir, baseRef);
36
+ const h2 = computeDiffHash(dir, baseRef);
37
+ expect(h1).toBe(h2);
38
+ expect(h1).toMatch(/^[0-9a-f]{64}$/);
39
+ });
40
+ it('returns identical hash when an amend produces the same patch under a new SHA', () => {
41
+ writeFileSync(join(dir, 'file.txt'), 'modified content\n');
42
+ git('commit --quiet -am edit', dir);
43
+ const shaBefore = git('rev-parse HEAD', dir).trim();
44
+ const hashBefore = computeDiffHash(dir, baseRef);
45
+ // Amend the commit message only — same tree, new SHA
46
+ git('commit --quiet --amend -m "edit (reworded)"', dir);
47
+ const shaAfter = git('rev-parse HEAD', dir).trim();
48
+ const hashAfter = computeDiffHash(dir, baseRef);
49
+ expect(shaAfter).not.toBe(shaBefore);
50
+ expect(hashAfter).toBe(hashBefore);
51
+ });
52
+ it('returns different hash when the diff content changes', () => {
53
+ writeFileSync(join(dir, 'file.txt'), 'first change\n');
54
+ git('commit --quiet -am one', dir);
55
+ const h1 = computeDiffHash(dir, baseRef);
56
+ writeFileSync(join(dir, 'file.txt'), 'second change\n');
57
+ git('commit --quiet -am two', dir);
58
+ const h2 = computeDiffHash(dir, baseRef);
59
+ expect(h1).not.toBe(h2);
60
+ });
61
+ it('returns the empty-diff hash when HEAD equals base', () => {
62
+ const h = computeDiffHash(dir, baseRef);
63
+ expect(h).toMatch(/^[0-9a-f]{64}$/);
64
+ // Hash of empty string sha256 — stable sentinel
65
+ expect(h).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
66
+ });
67
+ });
68
+ describe('PersistentDiffHashMap', () => {
69
+ let cacheFile;
70
+ beforeEach(() => {
71
+ cacheFile = join(mkdtempSync(join(tmpdir(), 'crosscheck-dhm-')), 'diff-hashes.json');
72
+ });
73
+ afterEach(() => {
74
+ if (existsSync(cacheFile))
75
+ rmSync(cacheFile);
76
+ });
77
+ it('upserts and reads back an entry', () => {
78
+ const m = new PersistentDiffHashMap(cacheFile);
79
+ m.upsert('owner/repo#1', { sha: 'abc123', hash: 'h1' });
80
+ expect(m.get('owner/repo#1')).toEqual({ sha: 'abc123', hash: 'h1' });
81
+ });
82
+ it('overwrites an existing entry for the same key', () => {
83
+ const m = new PersistentDiffHashMap(cacheFile);
84
+ m.upsert('owner/repo#1', { sha: 'abc', hash: 'h1' });
85
+ m.upsert('owner/repo#1', { sha: 'def', hash: 'h2' });
86
+ expect(m.get('owner/repo#1')).toEqual({ sha: 'def', hash: 'h2' });
87
+ });
88
+ it('persists across instances backed by the same file', () => {
89
+ const a = new PersistentDiffHashMap(cacheFile);
90
+ a.upsert('owner/repo#42', { sha: 'aaa', hash: 'hash-x' });
91
+ const b = new PersistentDiffHashMap(cacheFile);
92
+ expect(b.get('owner/repo#42')).toEqual({ sha: 'aaa', hash: 'hash-x' });
93
+ });
94
+ it('returns undefined for missing keys', () => {
95
+ const m = new PersistentDiffHashMap(cacheFile);
96
+ expect(m.get('nope')).toBeUndefined();
97
+ });
98
+ it('evicts oldest entries when exceeding the cap (FIFO)', () => {
99
+ const m = new PersistentDiffHashMap(cacheFile, /*maxEntries*/ 3);
100
+ m.upsert('a', { sha: '1', hash: 'h1' });
101
+ m.upsert('b', { sha: '2', hash: 'h2' });
102
+ m.upsert('c', { sha: '3', hash: 'h3' });
103
+ m.upsert('d', { sha: '4', hash: 'h4' }); // evicts 'a'
104
+ expect(m.get('a')).toBeUndefined();
105
+ expect(m.get('b')).toEqual({ sha: '2', hash: 'h2' });
106
+ expect(m.get('d')).toEqual({ sha: '4', hash: 'h4' });
107
+ });
108
+ it('upsert that overwrites does not count toward the cap', () => {
109
+ const m = new PersistentDiffHashMap(cacheFile, 2);
110
+ m.upsert('a', { sha: '1', hash: 'h1' });
111
+ m.upsert('b', { sha: '2', hash: 'h2' });
112
+ m.upsert('a', { sha: '1b', hash: 'h1b' }); // overwrite, not insert
113
+ expect(m.get('a')).toEqual({ sha: '1b', hash: 'h1b' });
114
+ expect(m.get('b')).toEqual({ sha: '2', hash: 'h2' });
115
+ });
116
+ it('tolerates a missing or malformed cache file', () => {
117
+ writeFileSync(cacheFile, 'not json');
118
+ const m = new PersistentDiffHashMap(cacheFile);
119
+ expect(m.get('any')).toBeUndefined();
120
+ m.upsert('x', { sha: '9', hash: 'h9' });
121
+ // Should have rewritten the file to valid JSON
122
+ const parsed = JSON.parse(readFileSync(cacheFile, 'utf8'));
123
+ expect(parsed['x']).toEqual({ sha: '9', hash: 'h9' });
124
+ });
125
+ });
126
+ //# sourceMappingURL=diff-hash.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-hash.test.js","sourceRoot":"","sources":["../../src/__tests__/diff-hash.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE5E,SAAS,GAAG,CAAC,IAAY,EAAE,GAAW;IACpC,OAAO,QAAQ,CAAC,OAAO,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;AAC9F,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAA;IAChE,GAAG,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAA;IAChC,GAAG,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;IAC7C,GAAG,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAA;IACjC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAA;IACtD,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IACjB,GAAG,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAA;IAClC,4EAA4E;IAC5E,GAAG,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAA;IACpD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AACjC,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,GAAW,CAAA;IACf,IAAI,OAAe,CAAA;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,CAAC;QAAA,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,oBAAoB,CAAC,CAAA;QAC1D,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAA;QACnC,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACnB,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,oBAAoB,CAAC,CAAA;QAC1D,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAA;QACnC,MAAM,SAAS,GAAG,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;QACnD,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAEhD,qDAAqD;QACrD,GAAG,CAAC,6CAA6C,EAAE,GAAG,CAAC,CAAA;QACvD,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;QAClD,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAE/C,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACpC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAA;QACtD,GAAG,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAA;QAClC,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,iBAAiB,CAAC,CAAA;QACvD,GAAG,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAA;QAClC,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACvC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;QACnC,gDAAgD;QAChD,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAA;IACpF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,SAAiB,CAAA;IAErB,UAAU,CAAC,GAAG,EAAE;QACd,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,CAAC,SAAS,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACtE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,CAAC,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QACzD,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,CAAA;QAChE,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA,CAAE,aAAa;QACtD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;QAClC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;QACjD,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA,CAAE,wBAAwB;QACnE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACtD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QACpC,MAAM,CAAC,GAAG,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;QACpC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvC,+CAA+C;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAA4B,CAAA;QACrF,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACvD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=is-fresh-review-comment.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-fresh-review-comment.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/is-fresh-review-comment.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { isFreshReviewComment } from '../github/client.js';
3
+ describe('isFreshReviewComment', () => {
4
+ it('returns true for a current review annotation', () => {
5
+ expect(isFreshReviewComment('### Code Review by ⚡ Codex\n\nbody\n\n<!-- crosscheck: origin=claude reviewer=codex verdict=BLOCK type=review -->')).toBe(true);
6
+ });
7
+ it('returns false for a recheck annotation', () => {
8
+ expect(isFreshReviewComment('> Recheck of [original review](#issuecomment-1)\n\n### Code Review by ⚡ Codex\n\nbody\n\n<!-- crosscheck: origin=claude reviewer=codex verdict=NEEDS_WORK type=recheck -->')).toBe(false);
9
+ });
10
+ it('returns false for a fix_failed annotation', () => {
11
+ expect(isFreshReviewComment('⚠️ **Auto-fix failed**\n\nblah\n\n<!-- crosscheck: fix_failed -->')).toBe(false);
12
+ });
13
+ // Guards the regression Codex flagged on PR #149: the summary cards emitted by
14
+ // commit-mode fix and conflict-resolve must NOT classify as fresh reviews, or
15
+ // subsequent rechecks will backlink to the summary instead of the actual review.
16
+ it('returns false for a fix_applied summary annotation', () => {
17
+ expect(isFreshReviewComment('### ✅ Auto-fix applied\n\nPushed [`abc1234`](...)\n\n<!-- crosscheck: fix_applied -->')).toBe(false);
18
+ });
19
+ it('returns false for a conflict_resolved summary annotation', () => {
20
+ expect(isFreshReviewComment('### 🔀 Conflicts resolved\n\nResolved 2 conflicts\n\n<!-- crosscheck: conflict_resolved -->')).toBe(false);
21
+ });
22
+ it('returns false for a no_diff_change notice', () => {
23
+ expect(isFreshReviewComment('✓ No diff change since the last review (was `abc1234`, now `def5678`). Skipping re-review.\n\n<!-- crosscheck: no_diff_change prev_sha=abc sha=def -->')).toBe(false);
24
+ });
25
+ // Defensive: the annotation is the source of truth. Even when the body happens
26
+ // to contain the legacy "### Code Review by" header, a non-review annotation
27
+ // wins. This guards against any future bare annotation accidentally falling
28
+ // through to the legacy header check.
29
+ it('rejects a summary card even when the body coincidentally contains the legacy review header', () => {
30
+ expect(isFreshReviewComment('### ✅ Auto-fix applied\n\nFix references "### Code Review by ⚡ Codex" in passing.\n\n<!-- crosscheck: fix_applied -->')).toBe(false);
31
+ });
32
+ it('returns true for a legacy (pre-annotation) review with the header and no recheck prefix', () => {
33
+ expect(isFreshReviewComment('### Code Review by ⚡ Codex\n\nbody, no annotation')).toBe(true);
34
+ });
35
+ it('returns false for a legacy recheck identified by the "> Recheck of" prefix', () => {
36
+ expect(isFreshReviewComment('> Recheck of [original review](#issuecomment-1)\n\n### Code Review by ⚡ Codex\n\nbody, no annotation')).toBe(false);
37
+ });
38
+ // Guards Codex's PR #149 [P2]: 2026-05-18 annotated reviews (between commits
39
+ // 9a0c324 and 36c915d) carry `<!-- crosscheck: origin reviewer verdict -->`
40
+ // without a `type=` field. They must still classify as fresh reviews so
41
+ // recheck backlink discovery survives the upgrade.
42
+ it('returns true for a pre-type-era annotated review (annotation without type=, has reviewer=, has header)', () => {
43
+ expect(isFreshReviewComment('### Code Review by ⚡ Codex\n\nbody\n\n<!-- crosscheck: origin=claude reviewer=codex verdict=BLOCK -->')).toBe(true);
44
+ });
45
+ it('returns false for a pre-type-era annotated recheck (no type=, has reviewer=, "> Recheck of" prefix)', () => {
46
+ expect(isFreshReviewComment('> Recheck of [original review](#issuecomment-1)\n\n### Code Review by ⚡ Codex\n\nbody\n\n<!-- crosscheck: origin=claude reviewer=codex verdict=NEEDS_WORK -->')).toBe(false);
47
+ });
48
+ it('returns false for an unrelated human comment with neither annotation nor header', () => {
49
+ expect(isFreshReviewComment('LGTM, merging now.')).toBe(false);
50
+ });
51
+ // Guards Codex's PR #149 second-recheck [P2]: review bodies often quote
52
+ // other crosscheck marker names as part of the finding text (Codex itself
53
+ // referenced `<!-- crosscheck: fix_applied -->` while reviewing this PR).
54
+ // Classification must read the FOOTER annotation appended by
55
+ // postReviewComment, not the first occurrence in the body.
56
+ it('classifies by the footer annotation, ignoring earlier quoted markers in the body', () => {
57
+ const body = '### Code Review by ⚡ Codex\n\n'
58
+ + 'The finding references `<!-- crosscheck: fix_applied -->` as an example of '
59
+ + 'a non-review annotation.\n\n'
60
+ + '<!-- crosscheck: origin=claude reviewer=codex verdict=BLOCK type=review -->';
61
+ expect(isFreshReviewComment(body)).toBe(true);
62
+ });
63
+ it('classifies as recheck when the footer says recheck even if an earlier quoted annotation looks like a review', () => {
64
+ const body = '> Recheck of [original review](#issuecomment-1)\n\n'
65
+ + '### Code Review by ⚡ Codex\n\n'
66
+ + 'Quote of the prior review annotation: `<!-- crosscheck: origin=claude reviewer=codex verdict=BLOCK type=review -->`\n\n'
67
+ + '<!-- crosscheck: origin=claude reviewer=codex verdict=NEEDS_WORK type=recheck -->';
68
+ expect(isFreshReviewComment(body)).toBe(false);
69
+ });
70
+ });
71
+ //# sourceMappingURL=is-fresh-review-comment.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-fresh-review-comment.test.js","sourceRoot":"","sources":["../../src/__tests__/is-fresh-review-comment.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scopes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scopes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,101 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { dedupScopes } from '../lib/scopes.js';
3
+ describe('dedupScopes', () => {
4
+ it('drops repo scope when its owner is in orgs', () => {
5
+ const input = [
6
+ { org: 'codatta' },
7
+ { owner: 'codatta', repo: 'humanbased-monorepo' },
8
+ ];
9
+ const { scopes, dropped, fallbackRepos } = dedupScopes(input);
10
+ expect(scopes).toEqual([{ org: 'codatta' }]);
11
+ expect(dropped.get('codatta')).toEqual(['humanbased-monorepo']);
12
+ expect(fallbackRepos.get('codatta')).toEqual([
13
+ { owner: 'codatta', repo: 'humanbased-monorepo' },
14
+ ]);
15
+ });
16
+ it('keeps repo scope when its owner is not in orgs', () => {
17
+ const input = [
18
+ { org: 'codatta' },
19
+ { owner: 'beingzy', repo: 'founder-dashboard' },
20
+ ];
21
+ const { scopes, dropped, fallbackRepos } = dedupScopes(input);
22
+ expect(scopes).toEqual(input);
23
+ expect(dropped.size).toBe(0);
24
+ expect(fallbackRepos.size).toBe(0);
25
+ });
26
+ it('matches org and owner names case-insensitively while preserving display values', () => {
27
+ const input = [
28
+ { org: 'motivation-labs' },
29
+ { owner: 'Motivation-Labs', repo: 'crosscheck' },
30
+ ];
31
+ const { scopes, dropped, fallbackRepos } = dedupScopes(input);
32
+ expect(scopes).toEqual([{ org: 'motivation-labs' }]);
33
+ expect([...dropped.entries()]).toEqual([
34
+ ['motivation-labs', ['crosscheck']],
35
+ ]);
36
+ expect(fallbackRepos.get('motivation-labs')).toEqual([
37
+ { owner: 'Motivation-Labs', repo: 'crosscheck' },
38
+ ]);
39
+ });
40
+ it('preserves order: orgs first, then surviving repos in original order', () => {
41
+ const input = [
42
+ { org: 'Motivation-Labs' },
43
+ { owner: 'beingzy', repo: 'a' },
44
+ { owner: 'Motivation-Labs', repo: 'monorepo' },
45
+ { owner: 'beingzy', repo: 'b' },
46
+ { org: 'codatta' },
47
+ { owner: 'codatta', repo: 'symphony' },
48
+ ];
49
+ const { scopes, dropped, fallbackRepos } = dedupScopes(input);
50
+ expect(scopes).toEqual([
51
+ { org: 'Motivation-Labs' },
52
+ { owner: 'beingzy', repo: 'a' },
53
+ { owner: 'beingzy', repo: 'b' },
54
+ { org: 'codatta' },
55
+ ]);
56
+ expect([...dropped.entries()]).toEqual([
57
+ ['Motivation-Labs', ['monorepo']],
58
+ ['codatta', ['symphony']],
59
+ ]);
60
+ expect([...fallbackRepos.entries()]).toEqual([
61
+ ['Motivation-Labs', [{ owner: 'Motivation-Labs', repo: 'monorepo' }]],
62
+ ['codatta', [{ owner: 'codatta', repo: 'symphony' }]],
63
+ ]);
64
+ });
65
+ it('aggregates multiple drops per org', () => {
66
+ const input = [
67
+ { org: 'codatta' },
68
+ { owner: 'codatta', repo: 'a' },
69
+ { owner: 'codatta', repo: 'b' },
70
+ { owner: 'codatta', repo: 'c' },
71
+ ];
72
+ const { scopes, dropped } = dedupScopes(input);
73
+ expect(scopes).toEqual([{ org: 'codatta' }]);
74
+ expect(dropped.get('codatta')).toEqual(['a', 'b', 'c']);
75
+ });
76
+ it('returns empty dropped map and unchanged scopes when no orgs configured', () => {
77
+ const input = [
78
+ { owner: 'a', repo: 'x' },
79
+ { owner: 'b', repo: 'y' },
80
+ ];
81
+ const { scopes, dropped } = dedupScopes(input);
82
+ expect(scopes).toEqual(input);
83
+ expect(dropped.size).toBe(0);
84
+ });
85
+ it('handles empty input', () => {
86
+ const { scopes, dropped } = dedupScopes([]);
87
+ expect(scopes).toEqual([]);
88
+ expect(dropped.size).toBe(0);
89
+ });
90
+ it('does not drop org scopes themselves even when duplicated (no-op for duplicate orgs)', () => {
91
+ const input = [
92
+ { org: 'codatta' },
93
+ { org: 'codatta' },
94
+ ];
95
+ const { scopes, dropped } = dedupScopes(input);
96
+ // duplicate org scopes are out of dedupScopes' contract; only repo-vs-org collapse is in scope
97
+ expect(scopes).toEqual(input);
98
+ expect(dropped.size).toBe(0);
99
+ });
100
+ });
101
+ //# sourceMappingURL=scopes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.test.js","sourceRoot":"","sources":["../../src/__tests__/scopes.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AA2JA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,SAAc,iBAoqBlD"}
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AA6JA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,SAAc,iBA4zBlD"}