refactor: simplify mode system by removing Mode interface and registry (#899)

Replace the over-engineered Mode interface/registry/detector pattern with
straightforward inline logic. There are only 2 modes (tag and agent) and
the complexity wasn't justified.

- Delete Mode interface, registry, and prepare pass-through modules
- Export prepareTagMode() and prepareAgentMode() as standalone functions
- Inline trigger checking and mode dispatch in run.ts/prepare.ts
- Change generatePrompt/createPrompt to take modeName string instead of Mode
- Remove dead code (extractGitHubContext, unused detector helpers)
- Update CLAUDE.md to reflect new architecture
This commit is contained in:
Ashwin Bhat
2026-02-05 17:22:30 -08:00
committed by GitHub
parent f09dc9a6a3
commit 7057f3318b
16 changed files with 390 additions and 1300 deletions
+23 -165
View File
@@ -3,60 +3,13 @@
import { describe, test, expect } from "bun:test";
import {
generatePrompt,
generateDefaultPrompt,
getEventTypeAndContext,
buildAllowedToolsString,
buildDisallowedToolsString,
} from "../src/create-prompt";
import type { PreparedContext } from "../src/create-prompt";
import type { Mode } from "../src/modes/types";
describe("generatePrompt", () => {
// Create a mock tag mode that uses the default prompt
const mockTagMode: Mode = {
name: "tag",
description: "Tag mode",
shouldTrigger: () => true,
prepareContext: (context) => ({ mode: "tag", githubContext: context }),
getAllowedTools: () => [],
getDisallowedTools: () => [],
shouldCreateTrackingComment: () => true,
generatePrompt: (context, githubData, useCommitSigning) =>
generateDefaultPrompt(context, githubData, useCommitSigning),
prepare: async () => ({
commentId: 123,
branchInfo: {
baseBranch: "main",
currentBranch: "main",
claudeBranch: undefined,
},
mcpConfig: "{}",
claudeArgs: "",
}),
};
// Create a mock agent mode that passes through prompts
const mockAgentMode: Mode = {
name: "agent",
description: "Agent mode",
shouldTrigger: () => true,
prepareContext: (context) => ({ mode: "agent", githubContext: context }),
getAllowedTools: () => [],
getDisallowedTools: () => [],
shouldCreateTrackingComment: () => false,
generatePrompt: (context) => context.prompt || "",
prepare: async () => ({
commentId: undefined,
branchInfo: {
baseBranch: "main",
currentBranch: "main",
claudeBranch: undefined,
},
mcpConfig: "{}",
claudeArgs: "",
}),
};
const mockGitHubData = {
contextData: {
title: "Test PR",
@@ -181,12 +134,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("You are Claude, an AI assistant");
expect(prompt).toContain("<event_type>GENERAL_COMMENT</event_type>");
@@ -214,12 +162,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<event_type>PR_REVIEW</event_type>");
expect(prompt).toContain("<is_pr>true</is_pr>");
@@ -245,12 +188,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<event_type>ISSUE_CREATED</event_type>");
expect(prompt).toContain(
@@ -278,12 +216,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<event_type>ISSUE_ASSIGNED</event_type>");
expect(prompt).toContain(
@@ -310,12 +243,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<event_type>ISSUE_LABELED</event_type>");
expect(prompt).toContain(
@@ -341,12 +269,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<event_type>PULL_REQUEST</event_type>");
expect(prompt).toContain("<is_pr>true</is_pr>");
@@ -370,12 +293,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Verify prompt generates successfully without custom instructions
expect(prompt).toContain("@claude please fix this");
@@ -400,7 +318,7 @@ describe("generatePrompt", () => {
envVars,
mockGitHubData,
false,
mockAgentMode,
"agent",
);
// Agent mode: Prompt is passed through as-is
@@ -441,7 +359,7 @@ describe("generatePrompt", () => {
envVars,
mockGitHubData,
false,
mockAgentMode,
"agent",
);
// v1.0: Variables are NOT substituted - prompt is passed as-is to Claude Code
@@ -490,7 +408,7 @@ describe("generatePrompt", () => {
envVars,
issueGitHubData,
false,
mockAgentMode,
"agent",
);
// Agent mode: Prompt is passed through as-is
@@ -515,7 +433,7 @@ describe("generatePrompt", () => {
envVars,
mockGitHubData,
false,
mockAgentMode,
"agent",
);
// Agent mode: No substitution - passed as-is
@@ -539,12 +457,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("You are Claude, an AI assistant");
expect(prompt).toContain("<event_type>ISSUE_CREATED</event_type>");
@@ -567,12 +480,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
expect(prompt).toContain("<trigger_username>johndoe</trigger_username>");
// With commit signing disabled, co-author info appears in git commit instructions
@@ -594,12 +502,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain PR-specific instructions (git commands when not using signing)
expect(prompt).toContain("git push");
@@ -630,12 +533,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain Issue-specific instructions
expect(prompt).toContain(
@@ -674,12 +572,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain the actual branch name with timestamp
expect(prompt).toContain(
@@ -709,12 +602,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain branch-specific instructions like issues
expect(prompt).toContain(
@@ -752,12 +640,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain open PR instructions (git commands when not using signing)
expect(prompt).toContain("git push");
@@ -788,12 +671,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain new branch instructions
expect(prompt).toContain(
@@ -821,12 +699,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain new branch instructions
expect(prompt).toContain(
@@ -854,12 +727,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should contain new branch instructions
expect(prompt).toContain(
@@ -883,12 +751,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
false,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, false, "tag");
// Should have git command instructions
expect(prompt).toContain("Use git commands via the Bash tool");
@@ -917,12 +780,7 @@ describe("generatePrompt", () => {
},
};
const prompt = await generatePrompt(
envVars,
mockGitHubData,
true,
mockTagMode,
);
const prompt = await generatePrompt(envVars, mockGitHubData, true, "tag");
// Should have commit signing tool instructions
expect(prompt).toContain("mcp__github_file_ops__commit_files");