mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
fix: address PR review comments for v1.0 simplification
- Remove duplicate prompt field spread (line 160) - Remove async from generatePrompt since slash commands are handled by Claude Code - Add detailed comment explaining why prompt → agent mode logic - Remove entire slash-commands loader and directories as Claude Code handles natively - Simplify prompt generation to just pass through to Claude Code These changes align with v1.0 philosophy: GitHub Action is a thin wrapper that delegates everything to Claude Code for native handling.
This commit is contained in:
@@ -157,7 +157,6 @@ export function prepareContext(
|
||||
disallowedTools: disallowedTools.join(","),
|
||||
}),
|
||||
...(prompt && { prompt }),
|
||||
...(prompt && { prompt }),
|
||||
...(claudeBranch && { claudeBranch }),
|
||||
};
|
||||
|
||||
@@ -460,12 +459,12 @@ function getCommitInstructions(
|
||||
}
|
||||
}
|
||||
|
||||
export async function generatePrompt(
|
||||
export function generatePrompt(
|
||||
context: PreparedContext,
|
||||
githubData: FetchDataResult,
|
||||
useCommitSigning: boolean,
|
||||
mode: Mode,
|
||||
): Promise<string> {
|
||||
): string {
|
||||
// v1.0: Simply pass through the prompt to Claude Code
|
||||
// Claude Code handles slash commands natively
|
||||
const prompt = context.prompt || "";
|
||||
@@ -767,8 +766,8 @@ export async function createPrompt(
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
// Generate the prompt directly (now async due to slash commands)
|
||||
const promptContent = await generatePrompt(
|
||||
// Generate the prompt directly
|
||||
const promptContent = generatePrompt(
|
||||
preparedContext,
|
||||
githubData,
|
||||
context.inputs.useCommitSigning,
|
||||
|
||||
@@ -10,6 +10,12 @@ export type AutoDetectedMode = "tag" | "agent";
|
||||
|
||||
export function detectMode(context: GitHubContext): AutoDetectedMode {
|
||||
// If prompt is provided, always use agent mode
|
||||
// Reasoning: When users provide explicit instructions via the prompt parameter,
|
||||
// they want Claude to execute those instructions immediately without waiting for
|
||||
// @claude mentions or other triggers. This aligns with the v1.0 philosophy where
|
||||
// Claude Code handles everything - the GitHub Action is just a thin wrapper that
|
||||
// passes through prompts directly to Claude Code for native handling (including
|
||||
// slash commands). This provides the most direct and flexible interaction model.
|
||||
if (context.inputs?.prompt) {
|
||||
return "agent";
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
import { readFile, readdir, stat } from "fs/promises";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
export interface SlashCommandMetadata {
|
||||
tools?: string[];
|
||||
settings?: Record<string, any>;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface SlashCommand {
|
||||
name: string;
|
||||
metadata: SlashCommandMetadata;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ResolvedCommand {
|
||||
expandedPrompt: string;
|
||||
tools?: string[];
|
||||
settings?: Record<string, any>;
|
||||
}
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const COMMANDS_DIR = join(__dirname, "../../slash-commands");
|
||||
|
||||
export async function resolveSlashCommand(
|
||||
prompt: string,
|
||||
variables?: Record<string, string | undefined>,
|
||||
): Promise<ResolvedCommand> {
|
||||
if (!prompt.startsWith("/")) {
|
||||
return handleLegacyPrompts(prompt);
|
||||
}
|
||||
|
||||
const parts = prompt.slice(1).split(" ");
|
||||
const commandPath = parts[0];
|
||||
const args = parts.slice(1);
|
||||
|
||||
if (!commandPath) {
|
||||
return { expandedPrompt: prompt };
|
||||
}
|
||||
|
||||
const commandParts = commandPath.split("/");
|
||||
|
||||
try {
|
||||
const command = await loadCommand(commandParts);
|
||||
if (!command) {
|
||||
console.warn(`Slash command not found: ${commandPath}`);
|
||||
return { expandedPrompt: prompt };
|
||||
}
|
||||
|
||||
let expandedContent = command.content;
|
||||
|
||||
if (args.length > 0) {
|
||||
expandedContent = expandedContent.replace(/\{args\}/g, args.join(" "));
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
Object.entries(variables).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
const regex = new RegExp(`\\{${key}\\}`, "g");
|
||||
expandedContent = expandedContent.replace(regex, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
expandedPrompt: expandedContent,
|
||||
tools: command.metadata.tools,
|
||||
settings: command.metadata.settings,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error loading slash command: ${error}`);
|
||||
return { expandedPrompt: prompt };
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCommand(
|
||||
commandParts: string[],
|
||||
): Promise<SlashCommand | null> {
|
||||
const possiblePaths = [
|
||||
join(COMMANDS_DIR, ...commandParts) + ".md",
|
||||
join(COMMANDS_DIR, ...commandParts, "default.md"),
|
||||
join(COMMANDS_DIR, commandParts[0] + ".md"),
|
||||
];
|
||||
|
||||
for (const filePath of possiblePaths) {
|
||||
try {
|
||||
const fileContent = await readFile(filePath, "utf-8");
|
||||
return parseCommandFile(commandParts.join("/"), fileContent);
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseCommandFile(name: string, content: string): SlashCommand {
|
||||
let metadata: SlashCommandMetadata = {};
|
||||
let commandContent = content;
|
||||
|
||||
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
||||
if (frontmatterMatch && frontmatterMatch[1]) {
|
||||
try {
|
||||
const parsedYaml = yaml.load(frontmatterMatch[1]);
|
||||
if (parsedYaml && typeof parsedYaml === "object") {
|
||||
metadata = parsedYaml as SlashCommandMetadata;
|
||||
}
|
||||
commandContent = frontmatterMatch[2]?.trim() || content;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse frontmatter for command ${name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
metadata,
|
||||
content: commandContent,
|
||||
};
|
||||
}
|
||||
|
||||
export async function listAvailableCommands(): Promise<string[]> {
|
||||
const commands: string[] = [];
|
||||
|
||||
async function scanDirectory(dir: string, prefix = ""): Promise<void> {
|
||||
try {
|
||||
const entries = await readdir(dir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry);
|
||||
const entryStat = await stat(fullPath);
|
||||
|
||||
if (entryStat.isDirectory()) {
|
||||
await scanDirectory(fullPath, prefix ? `${prefix}/${entry}` : entry);
|
||||
} else if (entry.endsWith(".md")) {
|
||||
const commandName = entry.replace(".md", "");
|
||||
if (commandName !== "default") {
|
||||
commands.push(prefix ? `${prefix}/${commandName}` : commandName);
|
||||
} else if (prefix) {
|
||||
commands.push(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error scanning directory ${dir}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
await scanDirectory(COMMANDS_DIR);
|
||||
return commands.sort();
|
||||
}
|
||||
|
||||
function handleLegacyPrompts(prompt: string): ResolvedCommand {
|
||||
const legacyKeys = ["override_prompt", "direct_prompt"];
|
||||
for (const key of legacyKeys) {
|
||||
const envValue = process.env[key.toUpperCase()];
|
||||
if (envValue) {
|
||||
console.log(`Using legacy ${key} as prompt`);
|
||||
return { expandedPrompt: envValue };
|
||||
}
|
||||
}
|
||||
return { expandedPrompt: prompt };
|
||||
}
|
||||
Reference in New Issue
Block a user