edsger 0.44.0 → 0.45.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 (105) hide show
  1. package/dist/api/run-sheets.d.ts +22 -0
  2. package/dist/api/run-sheets.js +13 -0
  3. package/dist/commands/run-sheet/index.d.ts +6 -0
  4. package/dist/commands/run-sheet/index.js +48 -0
  5. package/dist/index.js +22 -0
  6. package/dist/phases/run-sheet/index.d.ts +39 -0
  7. package/dist/phases/run-sheet/index.js +297 -0
  8. package/dist/phases/run-sheet/render.d.ts +42 -0
  9. package/dist/phases/run-sheet/render.js +133 -0
  10. package/package.json +11 -4
  11. package/tsconfig.build.json +4 -0
  12. package/tsconfig.json +3 -8
  13. package/vitest.config.ts +12 -0
  14. package/dist/api/__tests__/app-store.test.d.ts +0 -7
  15. package/dist/api/__tests__/app-store.test.js +0 -60
  16. package/dist/api/__tests__/intelligence.test.d.ts +0 -11
  17. package/dist/api/__tests__/intelligence.test.js +0 -315
  18. package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
  19. package/dist/api/features/__tests__/feature-utils.test.js +0 -370
  20. package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
  21. package/dist/api/features/__tests__/status-updater.test.js +0 -88
  22. package/dist/commands/build/__tests__/build.test.d.ts +0 -5
  23. package/dist/commands/build/__tests__/build.test.js +0 -206
  24. package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
  25. package/dist/commands/build/__tests__/detect-project.test.js +0 -160
  26. package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
  27. package/dist/commands/build/__tests__/run-build.test.js +0 -433
  28. package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
  29. package/dist/commands/intelligence/__tests__/command.test.js +0 -48
  30. package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
  31. package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
  32. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
  33. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
  34. package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
  35. package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
  36. package/dist/config/__tests__/config.test.d.ts +0 -4
  37. package/dist/config/__tests__/config.test.js +0 -286
  38. package/dist/config/__tests__/feature-status.test.d.ts +0 -4
  39. package/dist/config/__tests__/feature-status.test.js +0 -111
  40. package/dist/errors/__tests__/index.test.d.ts +0 -4
  41. package/dist/errors/__tests__/index.test.js +0 -349
  42. package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
  43. package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
  44. package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
  45. package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
  46. package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
  47. package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
  48. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
  49. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
  50. package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
  51. package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
  52. package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
  53. package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
  54. package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
  55. package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
  56. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
  57. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
  58. package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
  59. package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
  60. package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
  61. package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
  62. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
  63. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
  64. package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
  65. package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
  66. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
  67. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
  68. package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
  69. package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
  70. package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
  71. package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
  72. package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
  73. package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
  74. package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
  75. package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
  76. package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
  77. package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
  78. package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
  79. package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
  80. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
  81. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
  82. package/dist/phases/release-sync/__tests__/github.test.d.ts +0 -9
  83. package/dist/phases/release-sync/__tests__/github.test.js +0 -123
  84. package/dist/phases/release-sync/__tests__/snapshot.test.d.ts +0 -8
  85. package/dist/phases/release-sync/__tests__/snapshot.test.js +0 -93
  86. package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
  87. package/dist/phases/smoke-test/__tests__/agent.test.js +0 -85
  88. package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
  89. package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
  90. package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
  91. package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
  92. package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
  93. package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
  94. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
  95. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
  96. package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
  97. package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
  98. package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
  99. package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
  100. package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
  101. package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
  102. package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
  103. package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
  104. package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
  105. package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
@@ -1,303 +0,0 @@
1
- import assert from 'node:assert';
2
- import { describe, it } from 'node:test';
3
- import { applyAssignments, findUnassignedFiles, removeDeletedFilesFromPRs, } from '../file-assigner.js';
4
- function makePR(overrides) {
5
- return {
6
- feature_id: 'feat-1',
7
- name: `PR ${overrides.sequence}`,
8
- description: null,
9
- branch_name: null,
10
- base_pr_id: null,
11
- pull_request_url: null,
12
- pull_request_number: null,
13
- compare_url: null,
14
- status: 'pending',
15
- last_synced_commit: null,
16
- files: null,
17
- created_by: 'user',
18
- created_at: '2026-01-01',
19
- updated_at: '2026-01-01',
20
- ...overrides,
21
- };
22
- }
23
- function cf(path, changeType = 'modified') {
24
- return { path, change_type: changeType };
25
- }
26
- // ============================================================
27
- // findUnassignedFiles
28
- // ============================================================
29
- void describe('findUnassignedFiles', () => {
30
- void it('returns all files when no PR has files', () => {
31
- const prs = [makePR({ id: '1', sequence: 1, files: null })];
32
- const result = findUnassignedFiles([cf('a.ts'), cf('b.ts')], prs);
33
- assert.deepStrictEqual(result, [cf('a.ts'), cf('b.ts')]);
34
- });
35
- void it('returns empty when all files are assigned', () => {
36
- const prs = [
37
- makePR({
38
- id: '1',
39
- sequence: 1,
40
- files: [
41
- { path: 'a.ts', change_type: 'modified' },
42
- { path: 'b.ts', change_type: 'added' },
43
- ],
44
- }),
45
- ];
46
- const result = findUnassignedFiles([cf('a.ts'), cf('b.ts', 'added')], prs);
47
- assert.deepStrictEqual(result, []);
48
- });
49
- void it('returns only unassigned files', () => {
50
- const prs = [
51
- makePR({
52
- id: '1',
53
- sequence: 1,
54
- files: [{ path: 'a.ts', change_type: 'modified' }],
55
- }),
56
- makePR({
57
- id: '2',
58
- sequence: 2,
59
- files: [{ path: 'b.ts', change_type: 'added' }],
60
- }),
61
- ];
62
- const result = findUnassignedFiles([cf('a.ts'), cf('b.ts'), cf('c.ts'), cf('d.ts', 'added')], prs);
63
- assert.deepStrictEqual(result, [cf('c.ts'), cf('d.ts', 'added')]);
64
- });
65
- void it('handles PRs with empty files array', () => {
66
- const prs = [
67
- makePR({ id: '1', sequence: 1, files: [] }),
68
- makePR({
69
- id: '2',
70
- sequence: 2,
71
- files: [{ path: 'a.ts', change_type: 'modified' }],
72
- }),
73
- ];
74
- const result = findUnassignedFiles([cf('a.ts'), cf('b.ts')], prs);
75
- assert.deepStrictEqual(result, [cf('b.ts')]);
76
- });
77
- void it('returns empty for empty changed files', () => {
78
- const prs = [
79
- makePR({
80
- id: '1',
81
- sequence: 1,
82
- files: [{ path: 'a.ts', change_type: 'modified' }],
83
- }),
84
- ];
85
- const result = findUnassignedFiles([], prs);
86
- assert.deepStrictEqual(result, []);
87
- });
88
- void it('handles files spread across multiple PRs', () => {
89
- const prs = [
90
- makePR({
91
- id: '1',
92
- sequence: 1,
93
- files: [
94
- { path: 'src/auth/login.ts', change_type: 'modified' },
95
- { path: 'src/auth/session.ts', change_type: 'modified' },
96
- ],
97
- }),
98
- makePR({
99
- id: '2',
100
- sequence: 2,
101
- files: [
102
- { path: 'src/api/users.ts', change_type: 'added' },
103
- { path: 'src/api/roles.ts', change_type: 'added' },
104
- ],
105
- }),
106
- ];
107
- const changedFiles = [
108
- cf('src/auth/login.ts'),
109
- cf('src/auth/session.ts'),
110
- cf('src/api/users.ts'),
111
- cf('src/api/roles.ts'),
112
- cf('src/auth/oauth.ts', 'added'),
113
- cf('README.md'),
114
- ];
115
- const result = findUnassignedFiles(changedFiles, prs);
116
- assert.deepStrictEqual(result, [
117
- cf('src/auth/oauth.ts', 'added'),
118
- cf('README.md'),
119
- ]);
120
- });
121
- });
122
- // ============================================================
123
- // applyAssignments
124
- // ============================================================
125
- void describe('applyAssignments', () => {
126
- void it('merges new files into existing PR files', async () => {
127
- const prs = [
128
- makePR({
129
- id: 'pr-1',
130
- sequence: 1,
131
- files: [{ path: 'a.ts', change_type: 'modified' }],
132
- }),
133
- ];
134
- const unassigned = [cf('b.ts', 'added')];
135
- const updates = [];
136
- const mockUpdater = (prId, u) => {
137
- updates.push({ prId, files: u.files });
138
- return Promise.resolve();
139
- };
140
- const count = await applyAssignments([{ file: 'b.ts', pr_id: 'pr-1' }], unassigned, prs, mockUpdater);
141
- assert.strictEqual(count, 1);
142
- assert.strictEqual(updates.length, 1);
143
- assert.strictEqual(updates[0].prId, 'pr-1');
144
- assert.deepStrictEqual(updates[0].files, [
145
- { path: 'a.ts', change_type: 'modified' },
146
- { path: 'b.ts', change_type: 'added' },
147
- ]);
148
- });
149
- void it('deduplicates files already in PR', async () => {
150
- const prs = [
151
- makePR({
152
- id: 'pr-1',
153
- sequence: 1,
154
- files: [{ path: 'a.ts', change_type: 'modified' }],
155
- }),
156
- ];
157
- const unassigned = [cf('a.ts')];
158
- const updates = [];
159
- const mockUpdater = (_prId, u) => {
160
- updates.push({ files: u.files });
161
- return Promise.resolve();
162
- };
163
- await applyAssignments([{ file: 'a.ts', pr_id: 'pr-1' }], unassigned, prs, mockUpdater);
164
- // File list should not have duplicates
165
- assert.deepStrictEqual(updates[0].files, [
166
- { path: 'a.ts', change_type: 'modified' },
167
- ]);
168
- });
169
- void it('skips unknown PR ids', async () => {
170
- const prs = [makePR({ id: 'pr-1', sequence: 1, files: [] })];
171
- const unassigned = [cf('x.ts', 'added')];
172
- let called = false;
173
- const mockUpdater = () => {
174
- called = true;
175
- return Promise.resolve();
176
- };
177
- const count = await applyAssignments([{ file: 'x.ts', pr_id: 'unknown-id' }], unassigned, prs, mockUpdater);
178
- assert.strictEqual(count, 0);
179
- assert.strictEqual(called, false);
180
- });
181
- void it('uses change_type from git, not a hardcoded default', async () => {
182
- const prs = [makePR({ id: 'pr-1', sequence: 1, files: [] })];
183
- const unassigned = [cf('new.ts', 'added')];
184
- const updates = [];
185
- const mockUpdater = (_prId, u) => {
186
- updates.push({ files: u.files });
187
- return Promise.resolve();
188
- };
189
- await applyAssignments([{ file: 'new.ts', pr_id: 'pr-1' }], unassigned, prs, mockUpdater);
190
- assert.strictEqual(updates[0].files[0].change_type, 'added');
191
- });
192
- void it('distributes files to multiple PRs', async () => {
193
- const prs = [
194
- makePR({ id: 'pr-1', sequence: 1, files: [] }),
195
- makePR({ id: 'pr-2', sequence: 2, files: [] }),
196
- ];
197
- const unassigned = [
198
- cf('a.ts', 'added'),
199
- cf('b.ts', 'modified'),
200
- ];
201
- const updates = [];
202
- const mockUpdater = (prId) => {
203
- updates.push({ prId });
204
- return Promise.resolve();
205
- };
206
- const count = await applyAssignments([
207
- { file: 'a.ts', pr_id: 'pr-1' },
208
- { file: 'b.ts', pr_id: 'pr-2' },
209
- ], unassigned, prs, mockUpdater);
210
- assert.strictEqual(count, 2);
211
- assert.deepStrictEqual(updates.map((u) => u.prId), ['pr-1', 'pr-2']);
212
- });
213
- void it('handles updater failure gracefully', async () => {
214
- const prs = [makePR({ id: 'pr-1', sequence: 1, files: [] })];
215
- const unassigned = [cf('a.ts')];
216
- const mockUpdater = () => {
217
- return Promise.reject(new Error('DB error'));
218
- };
219
- const count = await applyAssignments([{ file: 'a.ts', pr_id: 'pr-1' }], unassigned, prs, mockUpdater);
220
- assert.strictEqual(count, 0);
221
- });
222
- });
223
- // ============================================================
224
- // removeDeletedFilesFromPRs
225
- // ============================================================
226
- void describe('removeDeletedFilesFromPRs', () => {
227
- void it('removes deleted files from PR file lists', async () => {
228
- const prs = [
229
- makePR({
230
- id: 'pr-1',
231
- sequence: 1,
232
- files: [
233
- { path: 'keep.ts', change_type: 'modified' },
234
- { path: 'gone.ts', change_type: 'added' },
235
- ],
236
- }),
237
- ];
238
- const changedFiles = [
239
- cf('keep.ts'),
240
- cf('gone.ts', 'deleted'),
241
- ];
242
- const updates = [];
243
- const mockUpdater = (prId, u) => {
244
- updates.push({ prId, files: u.files });
245
- return Promise.resolve();
246
- };
247
- const count = await removeDeletedFilesFromPRs(changedFiles, prs, mockUpdater);
248
- assert.strictEqual(count, 1);
249
- assert.deepStrictEqual(updates[0].files, [
250
- { path: 'keep.ts', change_type: 'modified' },
251
- ]);
252
- });
253
- void it('returns 0 when no files are deleted', async () => {
254
- const prs = [
255
- makePR({
256
- id: 'pr-1',
257
- sequence: 1,
258
- files: [{ path: 'a.ts', change_type: 'modified' }],
259
- }),
260
- ];
261
- let called = false;
262
- const mockUpdater = () => {
263
- called = true;
264
- return Promise.resolve();
265
- };
266
- const count = await removeDeletedFilesFromPRs([cf('a.ts')], prs, mockUpdater);
267
- assert.strictEqual(count, 0);
268
- assert.strictEqual(called, false);
269
- });
270
- void it('skips PRs with no matching deleted files', async () => {
271
- const prs = [
272
- makePR({
273
- id: 'pr-1',
274
- sequence: 1,
275
- files: [{ path: 'a.ts', change_type: 'modified' }],
276
- }),
277
- makePR({
278
- id: 'pr-2',
279
- sequence: 2,
280
- files: [{ path: 'b.ts', change_type: 'added' }],
281
- }),
282
- ];
283
- const updates = [];
284
- const mockUpdater = (prId) => {
285
- updates.push(prId);
286
- return Promise.resolve();
287
- };
288
- await removeDeletedFilesFromPRs([cf('b.ts', 'deleted')], prs, mockUpdater);
289
- // Only pr-2 should be updated
290
- assert.deepStrictEqual(updates, ['pr-2']);
291
- });
292
- void it('handles PRs with null files', async () => {
293
- const prs = [makePR({ id: 'pr-1', sequence: 1, files: null })];
294
- let called = false;
295
- const mockUpdater = () => {
296
- called = true;
297
- return Promise.resolve();
298
- };
299
- const count = await removeDeletedFilesFromPRs([cf('a.ts', 'deleted')], prs, mockUpdater);
300
- assert.strictEqual(count, 0);
301
- assert.strictEqual(called, false);
302
- });
303
- });
@@ -1,157 +0,0 @@
1
- import assert from 'node:assert';
2
- import { describe, it } from 'node:test';
3
- import { buildLearnerPrompt } from '../checklist-learner.js';
4
- // ── helpers ──────────────────────────────────────────────────
5
- function makeThread(id, overrides) {
6
- return {
7
- id,
8
- isResolved: false,
9
- isOutdated: false,
10
- comments: {
11
- totalCount: 1,
12
- nodes: [
13
- {
14
- id: `${id}-c1`,
15
- author: { login: overrides?.author || 'reviewer' },
16
- body: overrides?.body || 'Please fix this',
17
- path: overrides?.path || 'src/index.ts',
18
- line: overrides && 'line' in overrides ? (overrides.line ?? null) : 10,
19
- url: `https://github.com/o/r/pull/1#${id}`,
20
- },
21
- ],
22
- },
23
- };
24
- }
25
- function makeMap(entries) {
26
- return new Map(entries);
27
- }
28
- // ── buildLearnerPrompt ──────────────────────────────────────
29
- void describe('buildLearnerPrompt', () => {
30
- void it('includes addressed comment count in header', () => {
31
- const comments = [
32
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
33
- { comment_id: 'comment_2', action: 'changed', reply: 'Done' },
34
- ];
35
- const threads = [makeThread('t1'), makeThread('t2')];
36
- const map = makeMap([
37
- ['comment_1', 't1'],
38
- ['comment_2', 't2'],
39
- ]);
40
- const prompt = buildLearnerPrompt(comments, threads, map);
41
- assert.ok(prompt.includes('2 review comment(s)'));
42
- });
43
- void it('includes reviewer, file path, line, and comment body', () => {
44
- const comments = [
45
- { comment_id: 'comment_1', action: 'changed', reply: 'Added null check' },
46
- ];
47
- const threads = [
48
- makeThread('t1', {
49
- path: 'src/auth.ts',
50
- line: 42,
51
- body: 'Missing null check here',
52
- author: 'alice',
53
- }),
54
- ];
55
- const map = makeMap([['comment_1', 't1']]);
56
- const prompt = buildLearnerPrompt(comments, threads, map);
57
- assert.ok(prompt.includes('src/auth.ts'));
58
- assert.ok(prompt.includes('42'));
59
- assert.ok(prompt.includes('@alice'));
60
- assert.ok(prompt.includes('Missing null check here'));
61
- assert.ok(prompt.includes('Added null check'));
62
- });
63
- void it('includes resolution text for each comment', () => {
64
- const comments = [
65
- {
66
- comment_id: 'comment_1',
67
- action: 'changed',
68
- reply: 'Refactored to use try/catch',
69
- },
70
- ];
71
- const threads = [makeThread('t1')];
72
- const map = makeMap([['comment_1', 't1']]);
73
- const prompt = buildLearnerPrompt(comments, threads, map);
74
- assert.ok(prompt.includes('**Resolution**: Refactored to use try/catch'));
75
- });
76
- void it('includes summary when provided', () => {
77
- const comments = [
78
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
79
- ];
80
- const threads = [makeThread('t1')];
81
- const map = makeMap([['comment_1', 't1']]);
82
- const prompt = buildLearnerPrompt(comments, threads, map, 'Improved error handling across 3 files');
83
- assert.ok(prompt.includes('## Overall Summary'));
84
- assert.ok(prompt.includes('Improved error handling across 3 files'));
85
- });
86
- void it('omits summary section when not provided', () => {
87
- const comments = [
88
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
89
- ];
90
- const threads = [makeThread('t1')];
91
- const map = makeMap([['comment_1', 't1']]);
92
- const prompt = buildLearnerPrompt(comments, threads, map);
93
- assert.ok(!prompt.includes('## Overall Summary'));
94
- });
95
- void it('handles comment with no matching thread gracefully', () => {
96
- const comments = [
97
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
98
- ];
99
- // Empty threads — no match for comment_1
100
- const map = makeMap([['comment_1', 'nonexistent']]);
101
- const prompt = buildLearnerPrompt(comments, [], map);
102
- // Should still include the comment section with resolution
103
- assert.ok(prompt.includes('## comment_1'));
104
- assert.ok(prompt.includes('**Resolution**: Fixed'));
105
- // Should NOT include reviewer info since thread was not found
106
- assert.ok(!prompt.includes('**Reviewer**'));
107
- });
108
- void it('handles comment_id not in map gracefully', () => {
109
- const comments = [
110
- { comment_id: 'comment_99', action: 'changed', reply: 'Fixed' },
111
- ];
112
- const prompt = buildLearnerPrompt(comments, [], new Map());
113
- assert.ok(prompt.includes('## comment_99'));
114
- assert.ok(prompt.includes('**Resolution**: Fixed'));
115
- });
116
- void it('omits line when null', () => {
117
- const comments = [
118
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
119
- ];
120
- const threads = [makeThread('t1', { line: null })];
121
- const map = makeMap([['comment_1', 't1']]);
122
- const prompt = buildLearnerPrompt(comments, threads, map);
123
- assert.ok(prompt.includes('**File**: src/index.ts'));
124
- assert.ok(!prompt.includes('**Line**'));
125
- });
126
- void it('uses threadById map correctly for multiple comments', () => {
127
- const comments = [
128
- { comment_id: 'comment_1', action: 'changed', reply: 'Fix A' },
129
- { comment_id: 'comment_3', action: 'changed', reply: 'Fix C' },
130
- ];
131
- const threads = [
132
- makeThread('t1', { body: 'Issue A', path: 'a.ts' }),
133
- makeThread('t2', { body: 'Issue B', path: 'b.ts' }),
134
- makeThread('t3', { body: 'Issue C', path: 'c.ts' }),
135
- ];
136
- const map = makeMap([
137
- ['comment_1', 't1'],
138
- ['comment_2', 't2'],
139
- ['comment_3', 't3'],
140
- ]);
141
- const prompt = buildLearnerPrompt(comments, threads, map);
142
- assert.ok(prompt.includes('Issue A'));
143
- assert.ok(prompt.includes('a.ts'));
144
- assert.ok(prompt.includes('Issue C'));
145
- assert.ok(prompt.includes('c.ts'));
146
- // comment_2 was not in addressedComments, should not appear
147
- assert.ok(!prompt.includes('Issue B'));
148
- });
149
- void it('includes instructions section', () => {
150
- const comments = [
151
- { comment_id: 'comment_1', action: 'changed', reply: 'Fixed' },
152
- ];
153
- const prompt = buildLearnerPrompt(comments, [makeThread('t1')], makeMap([['comment_1', 't1']]));
154
- assert.ok(prompt.includes('## Instructions'));
155
- assert.ok(prompt.includes('code_review checklists'));
156
- });
157
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,116 +0,0 @@
1
- import assert from 'node:assert';
2
- import { describe, it } from 'node:test';
3
- import { createResolveSystemPrompt, createResolveUserPrompt, } from '../prompts.js';
4
- function makeThread(id, overrides) {
5
- const nodes = [
6
- {
7
- id: `${id}-comment-1`,
8
- author: { login: overrides?.author || 'reviewer' },
9
- body: overrides?.body || 'Please fix this',
10
- path: overrides?.path || 'src/index.ts',
11
- line: overrides?.line ?? 10,
12
- url: `https://github.com/owner/repo/pull/1#discussion_${id}`,
13
- },
14
- ];
15
- if (overrides?.followUps) {
16
- for (const fu of overrides.followUps) {
17
- nodes.push({
18
- id: `${id}-followup`,
19
- author: { login: fu.author },
20
- body: fu.body,
21
- path: overrides?.path || 'src/index.ts',
22
- line: overrides?.line ?? 10,
23
- url: `https://github.com/owner/repo/pull/1#discussion_${id}_fu`,
24
- });
25
- }
26
- }
27
- return {
28
- id,
29
- isResolved: false,
30
- isOutdated: false,
31
- comments: {
32
- totalCount: nodes.length,
33
- nodes,
34
- },
35
- };
36
- }
37
- void describe('createResolveSystemPrompt', () => {
38
- void it('includes decision criteria', () => {
39
- const prompt = createResolveSystemPrompt();
40
- assert.ok(prompt.includes('Make the change when'));
41
- assert.ok(prompt.includes('Skip the change when'));
42
- });
43
- void it('specifies comment_id format', () => {
44
- const prompt = createResolveSystemPrompt();
45
- assert.ok(prompt.includes('comment_id'));
46
- assert.ok(prompt.includes('comment_1'));
47
- });
48
- void it('includes result format', () => {
49
- const prompt = createResolveSystemPrompt();
50
- assert.ok(prompt.includes('resolve_result'));
51
- assert.ok(prompt.includes('"action"'));
52
- assert.ok(prompt.includes('"reply"'));
53
- });
54
- });
55
- void describe('createResolveUserPrompt', () => {
56
- void it('uses sequential comment IDs not thread IDs', () => {
57
- const threads = [makeThread('PRRT_kwDOxx_1'), makeThread('PRRT_kwDOxx_2')];
58
- const { prompt, commentIdToThreadId } = createResolveUserPrompt(threads);
59
- // Should use comment_1, comment_2 in the prompt
60
- assert.ok(prompt.includes('## comment_1'));
61
- assert.ok(prompt.includes('## comment_2'));
62
- // Should NOT include opaque GraphQL IDs
63
- assert.ok(!prompt.includes('PRRT_kwDOxx_1'));
64
- assert.ok(!prompt.includes('PRRT_kwDOxx_2'));
65
- // Mapping should be correct
66
- assert.strictEqual(commentIdToThreadId.get('comment_1'), 'PRRT_kwDOxx_1');
67
- assert.strictEqual(commentIdToThreadId.get('comment_2'), 'PRRT_kwDOxx_2');
68
- assert.strictEqual(commentIdToThreadId.size, 2);
69
- });
70
- void it('includes file path and line number', () => {
71
- const threads = [makeThread('t1', { path: 'src/auth.ts', line: 42 })];
72
- const { prompt } = createResolveUserPrompt(threads);
73
- assert.ok(prompt.includes('src/auth.ts'));
74
- assert.ok(prompt.includes('42'));
75
- });
76
- void it('includes comment body', () => {
77
- const threads = [
78
- makeThread('t1', { body: 'This should use a const instead of let' }),
79
- ];
80
- const { prompt } = createResolveUserPrompt(threads);
81
- assert.ok(prompt.includes('This should use a const instead of let'));
82
- });
83
- void it('includes follow-up comments', () => {
84
- const threads = [
85
- makeThread('t1', {
86
- body: 'Main comment',
87
- followUps: [{ author: 'dev', body: 'I disagree because...' }],
88
- }),
89
- ];
90
- const { prompt } = createResolveUserPrompt(threads);
91
- assert.ok(prompt.includes('Main comment'));
92
- assert.ok(prompt.includes('I disagree because...'));
93
- assert.ok(prompt.includes('@dev'));
94
- });
95
- void it('includes instruction to use exact comment IDs', () => {
96
- const threads = [makeThread('t1'), makeThread('t2'), makeThread('t3')];
97
- const { prompt } = createResolveUserPrompt(threads);
98
- assert.ok(prompt.includes('comment_1, comment_2, comment_3'));
99
- });
100
- void it('handles threads with no comments gracefully', () => {
101
- const emptyThread = {
102
- id: 'empty',
103
- isResolved: false,
104
- isOutdated: false,
105
- comments: { totalCount: 0, nodes: [] },
106
- };
107
- const { commentIdToThreadId } = createResolveUserPrompt([emptyThread]);
108
- // Empty thread should be skipped (no comment nodes to index)
109
- assert.strictEqual(commentIdToThreadId.size, 0);
110
- });
111
- void it('returns correct count in header', () => {
112
- const threads = [makeThread('t1'), makeThread('t2')];
113
- const { prompt } = createResolveUserPrompt(threads);
114
- assert.ok(prompt.includes('2 unresolved review comment(s)'));
115
- });
116
- });