Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const AppConfigSchema = z.object({
coderToken: z.string().min(1),
coderTaskNamePrefix: z.string().min(1).default("gh"),
coderTemplateName: z.string().min(1).default("task-template"),
coderTemplateNameCodex: z.string().min(1).default("task-template-codex"),
coderTemplatePreset: z.string().min(1).optional(),
coderOrganization: z.string().min(1).default("default"),
logFormat: z.string().optional(),
Expand All @@ -35,6 +36,7 @@ export function loadConfig(env: Record<string, string | undefined>): AppConfig {
coderToken: env.CODER_TOKEN,
coderTaskNamePrefix: env.CODER_TASK_NAME_PREFIX,
coderTemplateName: env.CODER_TEMPLATE_NAME,
coderTemplateNameCodex: env.CODER_TEMPLATE_NAME_CODEX,
coderTemplatePreset: env.CODER_TEMPLATE_PRESET,
coderOrganization: env.CODER_ORGANIZATION,
logFormat: env.LOG_FORMAT,
Expand Down
3 changes: 3 additions & 0 deletions src/handler-dispatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const mockConfig: AppConfig = {
coderToken: "coder-token",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
port: 3000,
};
Expand Down Expand Up @@ -92,6 +93,8 @@ describe("HandlerDispatcher", () => {
const createTaskContext = {
issueNumber: 42,
issueUrl: "https://github.com/xmtp/test-repo/issues/42",
issueTitle: "Fix some bug",
issueLabels: [] as string[],
repoName: "test-repo",
repoOwner: "xmtp",
senderLogin: "human-dev",
Expand Down
3 changes: 3 additions & 0 deletions src/handler-dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class HandlerDispatcher {
coderToken: this.options.config.coderToken,
coderTaskNamePrefix: this.options.config.coderTaskNamePrefix,
coderTemplateName: this.options.config.coderTemplateName,
coderTemplateNameCodex: this.options.config.coderTemplateNameCodex,
coderTemplatePreset: this.options.config.coderTemplatePreset,
coderOrganization: this.options.config.coderOrganization,
agentGithubUsername: this.options.config.agentGithubUsername,
Expand All @@ -73,6 +74,8 @@ export class HandlerDispatcher {
repo: ctx.repoName,
issueNumber: ctx.issueNumber,
issueUrl: ctx.issueUrl,
issueTitle: ctx.issueTitle,
issueLabels: ctx.issueLabels,
senderLogin: ctx.senderLogin,
},
logger,
Expand Down
1 change: 1 addition & 0 deletions src/handlers/close-task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const baseInputs: HandlerConfig = {
coderUsername: "coder-agent",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
agentGithubUsername: "xmtp-coder-agent",
};
Expand Down
91 changes: 91 additions & 0 deletions src/handlers/create-task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const baseInputs: HandlerConfig = {
coderUsername: "coder-agent",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
agentGithubUsername: "xmtp-coder-agent",
};
Expand All @@ -24,6 +25,8 @@ const issueContext = {
repo: "libxmtp",
issueNumber: 42,
issueUrl: "https://github.com/xmtp/libxmtp/issues/42",
issueTitle: "Fix some bug",
issueLabels: [],
senderLogin: "human-dev",
};

Expand Down Expand Up @@ -185,6 +188,94 @@ describe("CreateTaskHandler", () => {
expect(String(taskNameArg)).toBe("gh-libxmtp-42");
});

test("uses codex template when issue title contains codex", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

const ctx = {
...issueContext,
issueTitle: "Add Codex Support",
};
const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
ctx,
logger,
);
await handler.run();

const templateCall = coder.getTemplateByOrganizationAndName.mock
.calls[0] as unknown as [string, string];
expect(templateCall[1]).toBe("task-template-codex");
});

test("uses codex template when issue has codex label", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

const ctx = {
...issueContext,
issueLabels: ["enhancement", "codex"],
};
const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
ctx,
logger,
);
await handler.run();

const templateCall = coder.getTemplateByOrganizationAndName.mock
.calls[0] as unknown as [string, string];
expect(templateCall[1]).toBe("task-template-codex");
});

test("uses default template when no codex indicator", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
issueContext,
logger,
);
await handler.run();

const templateCall = coder.getTemplateByOrganizationAndName.mock
.calls[0] as unknown as [string, string];
expect(templateCall[1]).toBe("task-template");
});

test("codex match in title is case insensitive", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

const ctx = {
...issueContext,
issueTitle: "Use CODEX for processing",
};
const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
ctx,
logger,
);
await handler.run();

const templateCall = coder.getTemplateByOrganizationAndName.mock
.calls[0] as unknown as [string, string];
expect(templateCall[1]).toBe("task-template-codex");
});

test("logs task name via injected logger", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
Expand Down
19 changes: 18 additions & 1 deletion src/handlers/create-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface IssueContext {
repo: string;
issueNumber: number;
issueUrl: string;
issueTitle: string;
issueLabels: string[];
senderLogin: string;
}

Expand Down Expand Up @@ -88,9 +90,10 @@ export class CreateTaskHandler {
: this.context.issueUrl;

// 5. Get template and create task
const templateName = this.resolveTemplateName();
const template = await this.coder.getTemplateByOrganizationAndName(
this.inputs.coderOrganization,
this.inputs.coderTemplateName,
templateName,
);

const presets = await this.coder.getTemplateVersionPresets(
Expand Down Expand Up @@ -136,6 +139,20 @@ export class CreateTaskHandler {
};
}

private resolveTemplateName(): string {
const titleHasCodex = /codex/i.test(this.context.issueTitle);
const labelsHaveCodex = this.context.issueLabels.some(
(label) => label.toLowerCase() === "codex",
);
if (titleHasCodex || labelsHaveCodex) {
this.logger.info(
`Using codex template: ${this.inputs.coderTemplateNameCodex}`,
);
return this.inputs.coderTemplateNameCodex;
}
return this.inputs.coderTemplateName;
}

private generateTaskUrl(coderUsername: string, taskId: string): string {
const baseURL = this.inputs.coderURL.replace(/\/$/, "");
return `${baseURL}/tasks/${coderUsername}/${taskId}`;
Expand Down
1 change: 1 addition & 0 deletions src/handlers/failed-check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const baseInputs: HandlerConfig = {
coderUsername: "coder-agent",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
agentGithubUsername: "xmtp-coder-agent",
};
Expand Down
1 change: 1 addition & 0 deletions src/handlers/issue-comment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const baseInputs: HandlerConfig = {
coderUsername: "coder-agent",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
agentGithubUsername: "xmtp-coder-agent",
};
Expand Down
1 change: 1 addition & 0 deletions src/handlers/pr-comment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const baseInputs: HandlerConfig = {
coderUsername: "coder-agent",
coderTaskNamePrefix: "gh",
coderTemplateName: "task-template",
coderTemplateNameCodex: "task-template-codex",
coderOrganization: "default",
agentGithubUsername: "xmtp-coder-agent",
};
Expand Down
1 change: 1 addition & 0 deletions src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface HandlerConfig {
coderToken: string;
coderTaskNamePrefix: string;
coderTemplateName: string;
coderTemplateNameCodex: string;
coderTemplatePreset?: string;
coderOrganization: string;
agentGithubUsername: string; // replaces coderGithubUsername
Expand Down
6 changes: 6 additions & 0 deletions src/webhook-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export type HandlerType =
export type CreateTaskContext = {
issueNumber: number;
issueUrl: string;
issueTitle: string;
issueLabels: string[];
repoName: string;
repoOwner: string;
senderLogin: string;
Expand Down Expand Up @@ -243,6 +245,10 @@ export class WebhookRouter {
context: {
issueNumber: payload.issue.number,
issueUrl: payload.issue.html_url,
issueTitle: payload.issue.title,
issueLabels: (payload.issue.labels ?? []).map((l) =>
typeof l === "string" ? l : (l.name ?? ""),
),
repoName: payload.repository.name,
repoOwner: payload.repository.owner.login,
senderLogin: payload.sender.login,
Expand Down
Loading