hungry-ghost-hive 0.40.3 → 0.41.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.
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +8 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/manager/auto-assignment.js +4 -4
- package/dist/cli/commands/manager/auto-assignment.js.map +1 -1
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.d.ts +2 -0
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.d.ts.map +1 -0
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js +462 -0
- package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js.map +1 -0
- package/dist/cli/commands/manager/index.d.ts +12 -0
- package/dist/cli/commands/manager/index.d.ts.map +1 -1
- package/dist/cli/commands/manager/index.js +171 -3
- package/dist/cli/commands/manager/index.js.map +1 -1
- package/dist/cli/wizard/init-wizard.d.ts +2 -1
- package/dist/cli/wizard/init-wizard.d.ts.map +1 -1
- package/dist/cli/wizard/init-wizard.js +56 -3
- package/dist/cli/wizard/init-wizard.js.map +1 -1
- package/dist/cli/wizard/init-wizard.test.js +56 -9
- package/dist/cli/wizard/init-wizard.test.js.map +1 -1
- package/dist/config/schema.d.ts +389 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +18 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/context-files/index.test.js +6 -0
- package/dist/context-files/index.test.js.map +1 -1
- package/dist/git/github.d.ts +9 -0
- package/dist/git/github.d.ts.map +1 -1
- package/dist/git/github.js +15 -0
- package/dist/git/github.js.map +1 -1
- package/dist/git/github.test.js +77 -0
- package/dist/git/github.test.js.map +1 -1
- package/dist/orchestrator/scheduler.d.ts.map +1 -1
- package/dist/orchestrator/scheduler.js +1 -0
- package/dist/orchestrator/scheduler.js.map +1 -1
- package/dist/tmux/manager.d.ts.map +1 -1
- package/dist/tmux/manager.js +15 -8
- package/dist/tmux/manager.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.ts +8 -0
- package/src/cli/commands/manager/auto-assignment.ts +4 -4
- package/src/cli/commands/manager/auto-reject-comment-only-reviews.test.ts +589 -0
- package/src/cli/commands/manager/index.ts +228 -3
- package/src/cli/wizard/init-wizard.test.ts +62 -9
- package/src/cli/wizard/init-wizard.ts +66 -4
- package/src/config/schema.ts +20 -0
- package/src/context-files/index.test.ts +6 -0
- package/src/git/github.test.ts +93 -0
- package/src/git/github.ts +28 -0
- package/src/orchestrator/scheduler.ts +1 -0
- package/src/tmux/manager.ts +19 -8
package/src/git/github.test.ts
CHANGED
|
@@ -508,4 +508,97 @@ describe('github module', () => {
|
|
|
508
508
|
expect(result[0].author).toBe('reviewer1');
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
|
+
|
|
512
|
+
describe('getPullRequestComments', () => {
|
|
513
|
+
const workDir = '/test/repo';
|
|
514
|
+
const prNumber = 123;
|
|
515
|
+
|
|
516
|
+
it('should get PR comments with author, body, and createdAt', async () => {
|
|
517
|
+
mockedExeca.mockResolvedValueOnce({
|
|
518
|
+
stdout: JSON.stringify({
|
|
519
|
+
comments: [
|
|
520
|
+
{
|
|
521
|
+
author: { login: 'user1' },
|
|
522
|
+
body: 'This needs a fix in the error handling path.',
|
|
523
|
+
createdAt: '2026-03-01T10:00:00Z',
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
author: { login: 'user2' },
|
|
527
|
+
body: 'Coverage is below threshold.',
|
|
528
|
+
createdAt: '2026-03-01T11:00:00Z',
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
}),
|
|
532
|
+
stderr: '',
|
|
533
|
+
} as any);
|
|
534
|
+
|
|
535
|
+
const result = await github.getPullRequestComments(workDir, prNumber);
|
|
536
|
+
|
|
537
|
+
expect(result).toHaveLength(2);
|
|
538
|
+
expect(result[0]).toEqual({
|
|
539
|
+
author: 'user1',
|
|
540
|
+
body: 'This needs a fix in the error handling path.',
|
|
541
|
+
createdAt: '2026-03-01T10:00:00Z',
|
|
542
|
+
});
|
|
543
|
+
expect(result[1].author).toBe('user2');
|
|
544
|
+
expect(mockedExeca).toHaveBeenCalledWith('gh', ['pr', 'view', '123', '--json', 'comments'], {
|
|
545
|
+
cwd: workDir,
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('should return empty array when no comments exist', async () => {
|
|
550
|
+
mockedExeca.mockResolvedValueOnce({
|
|
551
|
+
stdout: JSON.stringify({ comments: [] }),
|
|
552
|
+
stderr: '',
|
|
553
|
+
} as any);
|
|
554
|
+
|
|
555
|
+
const result = await github.getPullRequestComments(workDir, prNumber);
|
|
556
|
+
|
|
557
|
+
expect(result).toHaveLength(0);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should handle missing comments field gracefully', async () => {
|
|
561
|
+
mockedExeca.mockResolvedValueOnce({
|
|
562
|
+
stdout: JSON.stringify({}),
|
|
563
|
+
stderr: '',
|
|
564
|
+
} as any);
|
|
565
|
+
|
|
566
|
+
const result = await github.getPullRequestComments(workDir, prNumber);
|
|
567
|
+
|
|
568
|
+
expect(result).toHaveLength(0);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('should handle comments with missing author fields', async () => {
|
|
572
|
+
mockedExeca.mockResolvedValueOnce({
|
|
573
|
+
stdout: JSON.stringify({
|
|
574
|
+
comments: [
|
|
575
|
+
{ body: 'Anonymous comment', createdAt: '2026-03-01T10:00:00Z' },
|
|
576
|
+
{ author: {}, body: 'No login', createdAt: '2026-03-01T11:00:00Z' },
|
|
577
|
+
],
|
|
578
|
+
}),
|
|
579
|
+
stderr: '',
|
|
580
|
+
} as any);
|
|
581
|
+
|
|
582
|
+
const result = await github.getPullRequestComments(workDir, prNumber);
|
|
583
|
+
|
|
584
|
+
expect(result).toHaveLength(2);
|
|
585
|
+
expect(result[0].author).toBe('unknown');
|
|
586
|
+
expect(result[1].author).toBe('unknown');
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('should handle comments with missing body and createdAt', async () => {
|
|
590
|
+
mockedExeca.mockResolvedValueOnce({
|
|
591
|
+
stdout: JSON.stringify({
|
|
592
|
+
comments: [{ author: { login: 'user1' } }],
|
|
593
|
+
}),
|
|
594
|
+
stderr: '',
|
|
595
|
+
} as any);
|
|
596
|
+
|
|
597
|
+
const result = await github.getPullRequestComments(workDir, prNumber);
|
|
598
|
+
|
|
599
|
+
expect(result).toHaveLength(1);
|
|
600
|
+
expect(result[0].body).toBe('');
|
|
601
|
+
expect(result[0].createdAt).toBe('');
|
|
602
|
+
});
|
|
603
|
+
});
|
|
511
604
|
});
|
package/src/git/github.ts
CHANGED
|
@@ -301,3 +301,31 @@ export async function getPullRequestReviews(
|
|
|
301
301
|
const data = JSON.parse(stdout);
|
|
302
302
|
return data.reviews || [];
|
|
303
303
|
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get PR comments (issue comments, not inline review comments).
|
|
307
|
+
* Returns comments with author login, body, and creation date.
|
|
308
|
+
*/
|
|
309
|
+
export async function getPullRequestComments(
|
|
310
|
+
workDir: string,
|
|
311
|
+
prNumber: number
|
|
312
|
+
): Promise<
|
|
313
|
+
Array<{
|
|
314
|
+
author: string;
|
|
315
|
+
body: string;
|
|
316
|
+
createdAt: string;
|
|
317
|
+
}>
|
|
318
|
+
> {
|
|
319
|
+
const { stdout } = await execa('gh', ['pr', 'view', prNumber.toString(), '--json', 'comments'], {
|
|
320
|
+
cwd: workDir,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const data = JSON.parse(stdout);
|
|
324
|
+
return (data.comments || []).map(
|
|
325
|
+
(c: { author?: { login?: string }; body?: string; createdAt?: string }) => ({
|
|
326
|
+
author: c.author?.login || 'unknown',
|
|
327
|
+
body: c.body || '',
|
|
328
|
+
createdAt: c.createdAt || '',
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
}
|
package/src/tmux/manager.ts
CHANGED
|
@@ -266,14 +266,25 @@ export async function sendToTmuxSession(
|
|
|
266
266
|
await new Promise(resolve => setTimeout(resolve, CLEAR_INPUT_DELAY_MS));
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
const isMultiLine = text.includes('\n');
|
|
270
|
+
|
|
271
|
+
if (isMultiLine) {
|
|
272
|
+
// Multi-line text: write to temp file and paste via $(cat ...) to avoid
|
|
273
|
+
// the [Pasted text #N] buffering issue in Claude CLI. The shell expands
|
|
274
|
+
// $(cat file) into a single argument, bypassing tmux's paste-buffer handling.
|
|
275
|
+
const tempFile = join(
|
|
276
|
+
tmpdir(),
|
|
277
|
+
`hive-nudge-${Date.now()}-${sessionName.replace(/[^a-zA-Z0-9-]/g, '_')}.txt`
|
|
278
|
+
);
|
|
279
|
+
writeFileSync(tempFile, text, 'utf-8');
|
|
280
|
+
const catCmd = `$(cat ${tempFile})`;
|
|
281
|
+
await execa('tmux', ['send-keys', '-t', sessionName, '-l', '--', catCmd]);
|
|
282
|
+
} else {
|
|
283
|
+
// Single-line: use send-keys with literal flag directly.
|
|
284
|
+
// '--' signals end of options, preventing text starting with '-' from being parsed as flags.
|
|
285
|
+
await execa('tmux', ['send-keys', '-t', sessionName, '-l', '--', text]);
|
|
286
|
+
}
|
|
287
|
+
|
|
277
288
|
// Send Enter as a key event (C-m = carriage return = Enter) to ensure prompt receives it
|
|
278
289
|
await execa('tmux', ['send-keys', '-t', sessionName, 'C-m']);
|
|
279
290
|
}
|