mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:37:51 +00:00
A GitHub App (Octokit + webhooks) built on @hanzo/ai (headless model client) and @hanzo/iam (identity). Reviews pull requests, triages new issues, and answers @hanzo mentions in issue/PR comments. Pure cores (network-free, exhaustively unit-tested): - webhook.ts X-Hub-Signature-256 HMAC verify (constant time) + event routing - review.ts diff windowing/truncation, prompt assembly, review formatting - triage.ts issue prompt, severity/label parsing (labels ∩ repo labels) - mention.ts thread-context answer prompt Impure edges compose them: config (env boundary), hanzo (thin @hanzo/ai — the one model path), github-api (thin Octokit .request wrappers), app (App + handlers → route → dispatch), server (health + graceful shutdown). 84 vitest tests green; tsc --noEmit clean; build succeeds. Webhook path /v1/github/webhooks (Hanzo /v1, never /api/). Secrets env-only.
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
buildMentionPrompt,
|
|
formatMentionComment,
|
|
mentionSystemPrompt,
|
|
type MentionContext,
|
|
} from '../src/mention.js';
|
|
|
|
const ctx: MentionContext = {
|
|
repo: 'hanzoai/extension',
|
|
number: 9,
|
|
isPullRequest: false,
|
|
title: 'How do I configure X?',
|
|
body: 'I cannot find the setting.',
|
|
comments: [
|
|
{ author: 'alice', body: 'Same here.' },
|
|
{ author: 'bob', body: 'Try the config file.' },
|
|
],
|
|
};
|
|
|
|
describe('buildMentionPrompt', () => {
|
|
it('carries the issue metadata, thread, and the question', () => {
|
|
const p = buildMentionPrompt(ctx, 'where is the setting?');
|
|
expect(p).toContain('issue hanzoai/extension#9');
|
|
expect(p).toContain('How do I configure X?');
|
|
expect(p).toContain('@alice: Same here.');
|
|
expect(p).toContain('@bob: Try the config file.');
|
|
expect(p).toContain('where is the setting?');
|
|
});
|
|
|
|
it('labels a PR thread as a pull request', () => {
|
|
const p = buildMentionPrompt({ ...ctx, isPullRequest: true }, 'q');
|
|
expect(p).toContain('pull request hanzoai/extension#9');
|
|
});
|
|
|
|
it('falls back to summarize for a bare mention', () => {
|
|
const p = buildMentionPrompt(ctx, '');
|
|
expect(p).toContain('Summarize the thread');
|
|
});
|
|
|
|
it('handles an empty thread', () => {
|
|
const p = buildMentionPrompt({ ...ctx, comments: [] }, 'q');
|
|
expect(p).toContain('(no prior comments)');
|
|
});
|
|
});
|
|
|
|
describe('formatMentionComment', () => {
|
|
it('appends the Hanzo attribution with model id', () => {
|
|
const out = formatMentionComment('Here is the answer.', 'zen5');
|
|
expect(out).toContain('Here is the answer.');
|
|
expect(out).toContain('_Answered by Hanzo AI (`zen5`)._');
|
|
});
|
|
});
|
|
|
|
describe('mentionSystemPrompt', () => {
|
|
it('frames a context-bound answer', () => {
|
|
expect(mentionSystemPrompt()).toContain('@hanzo');
|
|
expect(mentionSystemPrompt()).toContain('Do not invent facts');
|
|
});
|
|
});
|