mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 06:54:13 +08:00
feat: send user request as separate content block for slash command support (#785)
* feat: send user request as separate content block for slash command support When in tag mode with the SDK path, extracts the user's request from the trigger comment (text after @claude) and sends it as a separate content block. This enables the CLI to process slash commands like "/review-pr". - Add extract-user-request utility to parse trigger comments - Write user request to separate file during prompt generation - Send multi-block SDKUserMessage when user request file exists - Add tests for the extraction utility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: address PR feedback - Fix potential ReDoS vulnerability by using string operations instead of regex - Remove unused extractUserRequestFromEvent function and tests - Extract USER_REQUEST_FILENAME to shared constants - Conditionally log user request based on showFullOutput setting - Add JSDoc documentation to extractUserRequestFromContext --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,12 @@ import type { ParsedGitHubContext } from "../github/context";
|
||||
import type { CommonFields, PreparedContext, EventData } from "./types";
|
||||
import { GITHUB_SERVER_URL } from "../github/api/config";
|
||||
import type { Mode, ModeContext } from "../modes/types";
|
||||
import { extractUserRequest } from "../utils/extract-user-request";
|
||||
export type { CommonFields, PreparedContext } from "./types";
|
||||
|
||||
/** Filename for the user request file, read by the SDK runner */
|
||||
const USER_REQUEST_FILENAME = "claude-user-request.txt";
|
||||
|
||||
// Tag mode defaults - these tools are needed for tag mode to function
|
||||
const BASE_ALLOWED_TOOLS = [
|
||||
"Edit",
|
||||
@@ -847,6 +851,55 @@ f. If you are unable to complete certain steps, such as running a linter or test
|
||||
return promptContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the user's request from the prepared context and GitHub data.
|
||||
*
|
||||
* This is used to send the user's actual command/request as a separate
|
||||
* content block, enabling slash command processing in the CLI.
|
||||
*
|
||||
* @param context - The prepared context containing event data and trigger phrase
|
||||
* @param githubData - The fetched GitHub data containing issue/PR body content
|
||||
* @returns The extracted user request text (e.g., "/review-pr" or "fix this bug"),
|
||||
* or null for assigned/labeled events without an explicit trigger in the body
|
||||
*
|
||||
* @example
|
||||
* // Comment event: "@claude /review-pr" -> returns "/review-pr"
|
||||
* // Issue body with "@claude fix this" -> returns "fix this"
|
||||
* // Issue assigned without @claude in body -> returns null
|
||||
*/
|
||||
function extractUserRequestFromContext(
|
||||
context: PreparedContext,
|
||||
githubData: FetchDataResult,
|
||||
): string | null {
|
||||
const { eventData, triggerPhrase } = context;
|
||||
|
||||
// For comment events, extract from comment body
|
||||
if (
|
||||
"commentBody" in eventData &&
|
||||
eventData.commentBody &&
|
||||
(eventData.eventName === "issue_comment" ||
|
||||
eventData.eventName === "pull_request_review_comment" ||
|
||||
eventData.eventName === "pull_request_review")
|
||||
) {
|
||||
return extractUserRequest(eventData.commentBody, triggerPhrase);
|
||||
}
|
||||
|
||||
// For issue/PR events triggered by body content, extract from the body
|
||||
if (githubData.contextData?.body) {
|
||||
const request = extractUserRequest(
|
||||
githubData.contextData.body,
|
||||
triggerPhrase,
|
||||
);
|
||||
if (request) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
// For assigned/labeled events without explicit trigger in body,
|
||||
// return null to indicate the full context should be used
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function createPrompt(
|
||||
mode: Mode,
|
||||
modeContext: ModeContext,
|
||||
@@ -895,6 +948,22 @@ export async function createPrompt(
|
||||
promptContent,
|
||||
);
|
||||
|
||||
// Extract and write the user request separately for SDK multi-block messaging
|
||||
// This allows the CLI to process slash commands (e.g., "@claude /review-pr")
|
||||
const userRequest = extractUserRequestFromContext(
|
||||
preparedContext,
|
||||
githubData,
|
||||
);
|
||||
if (userRequest) {
|
||||
await writeFile(
|
||||
`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts/${USER_REQUEST_FILENAME}`,
|
||||
userRequest,
|
||||
);
|
||||
console.log("===== USER REQUEST =====");
|
||||
console.log(userRequest);
|
||||
console.log("========================");
|
||||
}
|
||||
|
||||
// Set allowed tools
|
||||
const hasActionsReadPermission = false;
|
||||
|
||||
|
||||
32
src/utils/extract-user-request.ts
Normal file
32
src/utils/extract-user-request.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Extracts the user's request from a trigger comment.
|
||||
*
|
||||
* Given a comment like "@claude /review-pr please check the auth module",
|
||||
* this extracts "/review-pr please check the auth module".
|
||||
*
|
||||
* @param commentBody - The full comment body containing the trigger phrase
|
||||
* @param triggerPhrase - The trigger phrase (e.g., "@claude")
|
||||
* @returns The user's request (text after the trigger phrase), or null if not found
|
||||
*/
|
||||
export function extractUserRequest(
|
||||
commentBody: string | undefined,
|
||||
triggerPhrase: string,
|
||||
): string | null {
|
||||
if (!commentBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use string operations instead of regex for better performance and security
|
||||
// (avoids potential ReDoS with large comment bodies)
|
||||
const triggerIndex = commentBody
|
||||
.toLowerCase()
|
||||
.indexOf(triggerPhrase.toLowerCase());
|
||||
if (triggerIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const afterTrigger = commentBody
|
||||
.substring(triggerIndex + triggerPhrase.length)
|
||||
.trim();
|
||||
return afterTrigger || null;
|
||||
}
|
||||
Reference in New Issue
Block a user