mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
New package @hanzo/gitlab mirrors @hanzo/github for the GitLab ecosystem (self-managed + gitlab.com). Webhook service over GitLab Project/Group webhooks + REST API v4, built on the published @hanzo/ai and @hanzo/iam. - webhook.ts: constant-time X-Gitlab-Token verify + X-Gitlab-Event routing (pure) - review.ts: GitLab changes → unified diff → windowing/truncation → note (pure) - triage.ts: issue → prompt; parse severity/labels ∩ project labels (pure) - mention.ts: MR/issue thread + @hanzo question → answer (pure) - gitlab-api.ts: thin REST v4 fetch wrappers (getMRChanges, listLabels, createMRNote/createIssueNote, addIssueLabels, getNotes) - hanzo.ts: one path to the model via @hanzo/ai (api.hanzo.ai /v1) - config.ts: env boundary (GITLAB_BASE_URL/TOKEN/WEBHOOK_SECRET, HANZO_API_KEY) - app.ts + server.ts: compose the pure cores; HTTP boundary with token gate, health probe, 202 ACK + graceful shutdown 97 vitest tests green, tsc --noEmit clean, build produces dist. Registered packages/gitlab in pnpm-workspace.yaml.
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
buildMentionPrompt,
|
|
formatMentionNote,
|
|
mentionSystemPrompt,
|
|
type MentionContext,
|
|
} from '../src/mention.js';
|
|
|
|
const ctx: MentionContext = {
|
|
project: 'hanzoai/extension',
|
|
iid: 9,
|
|
isMergeRequest: false,
|
|
title: 'How do I configure X?',
|
|
body: 'I cannot find the setting.',
|
|
notes: [
|
|
{ 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 an MR thread as a merge request', () => {
|
|
const p = buildMentionPrompt({ ...ctx, isMergeRequest: true }, 'q');
|
|
expect(p).toContain('merge 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, notes: [] }, 'q');
|
|
expect(p).toContain('(no prior notes)');
|
|
});
|
|
});
|
|
|
|
describe('formatMentionNote', () => {
|
|
it('appends the Hanzo attribution with model id', () => {
|
|
const out = formatMentionNote('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');
|
|
});
|
|
});
|