mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 14:24:13 +08:00
Two fixes for the Agent SDK path (USE_AGENT_SDK=true):
1. Add settingSources to load filesystem settings
- Without this, CLI-installed plugins aren't available to the SDK
- Also needed to load CLAUDE.md files from the project
2. Default systemPrompt to claude_code preset
- Without an explicit systemPrompt, the SDK would use no system prompt
- Now defaults to { type: "preset", preset: "claude_code" } to match CLI behavior
Also adds logging of SDK options (excluding env) for debugging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
150 lines
5.1 KiB
TypeScript
150 lines
5.1 KiB
TypeScript
import { parse as parseShellArgs } from "shell-quote";
|
|
import type { ClaudeOptions } from "./run-claude";
|
|
import type { Options as SdkOptions } from "@anthropic-ai/claude-agent-sdk";
|
|
|
|
/**
|
|
* Result of parsing ClaudeOptions for SDK usage
|
|
*/
|
|
export type ParsedSdkOptions = {
|
|
sdkOptions: SdkOptions;
|
|
showFullOutput: boolean;
|
|
hasJsonSchema: boolean;
|
|
};
|
|
|
|
// Flags that should accumulate multiple values instead of overwriting
|
|
const ACCUMULATING_FLAGS = new Set(["allowedTools", "disallowedTools"]);
|
|
|
|
/**
|
|
* Parse claudeArgs string into extraArgs record for SDK pass-through
|
|
* The SDK/CLI will handle --mcp-config, --json-schema, etc.
|
|
* For allowedTools and disallowedTools, multiple occurrences are accumulated (comma-joined).
|
|
*/
|
|
function parseClaudeArgsToExtraArgs(
|
|
claudeArgs?: string,
|
|
): Record<string, string | null> {
|
|
if (!claudeArgs?.trim()) return {};
|
|
|
|
const result: Record<string, string | null> = {};
|
|
const args = parseShellArgs(claudeArgs).filter(
|
|
(arg): arg is string => typeof arg === "string",
|
|
);
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (arg?.startsWith("--")) {
|
|
const flag = arg.slice(2);
|
|
const nextArg = args[i + 1];
|
|
|
|
// Check if next arg is a value (not another flag)
|
|
if (nextArg && !nextArg.startsWith("--")) {
|
|
// For accumulating flags, join multiple values with commas
|
|
if (ACCUMULATING_FLAGS.has(flag) && result[flag]) {
|
|
result[flag] = `${result[flag]},${nextArg}`;
|
|
} else {
|
|
result[flag] = nextArg;
|
|
}
|
|
i++; // Skip the value
|
|
} else {
|
|
result[flag] = null; // Boolean flag
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Parse ClaudeOptions into SDK-compatible options
|
|
* Uses extraArgs for CLI pass-through instead of duplicating option parsing
|
|
*/
|
|
export function parseSdkOptions(options: ClaudeOptions): ParsedSdkOptions {
|
|
// Determine output verbosity
|
|
const isDebugMode = process.env.ACTIONS_STEP_DEBUG === "true";
|
|
const showFullOutput = options.showFullOutput === "true" || isDebugMode;
|
|
|
|
// Parse claudeArgs into extraArgs for CLI pass-through
|
|
const extraArgs = parseClaudeArgsToExtraArgs(options.claudeArgs);
|
|
|
|
// Detect if --json-schema is present (for hasJsonSchema flag)
|
|
const hasJsonSchema = "json-schema" in extraArgs;
|
|
|
|
// Extract and merge allowedTools from both sources:
|
|
// 1. From extraArgs (parsed from claudeArgs - contains tag mode's tools)
|
|
// 2. From options.allowedTools (direct input - may be undefined)
|
|
// This prevents duplicate flags being overwritten when claudeArgs contains --allowedTools
|
|
const extraArgsAllowedTools = extraArgs["allowedTools"]
|
|
? extraArgs["allowedTools"].split(",").map((t) => t.trim())
|
|
: [];
|
|
const directAllowedTools = options.allowedTools
|
|
? options.allowedTools.split(",").map((t) => t.trim())
|
|
: [];
|
|
const mergedAllowedTools = [
|
|
...new Set([...extraArgsAllowedTools, ...directAllowedTools]),
|
|
];
|
|
delete extraArgs["allowedTools"];
|
|
|
|
// Same for disallowedTools
|
|
const extraArgsDisallowedTools = extraArgs["disallowedTools"]
|
|
? extraArgs["disallowedTools"].split(",").map((t) => t.trim())
|
|
: [];
|
|
const directDisallowedTools = options.disallowedTools
|
|
? options.disallowedTools.split(",").map((t) => t.trim())
|
|
: [];
|
|
const mergedDisallowedTools = [
|
|
...new Set([...extraArgsDisallowedTools, ...directDisallowedTools]),
|
|
];
|
|
delete extraArgs["disallowedTools"];
|
|
|
|
// Build custom environment
|
|
const env: Record<string, string | undefined> = { ...process.env };
|
|
if (process.env.INPUT_ACTION_INPUTS_PRESENT) {
|
|
env.GITHUB_ACTION_INPUTS = process.env.INPUT_ACTION_INPUTS_PRESENT;
|
|
}
|
|
|
|
// Build system prompt option - default to claude_code preset
|
|
let systemPrompt: SdkOptions["systemPrompt"];
|
|
if (options.systemPrompt) {
|
|
systemPrompt = options.systemPrompt;
|
|
} else if (options.appendSystemPrompt) {
|
|
systemPrompt = {
|
|
type: "preset",
|
|
preset: "claude_code",
|
|
append: options.appendSystemPrompt,
|
|
};
|
|
} else {
|
|
// Default to claude_code preset when no custom prompt is specified
|
|
systemPrompt = {
|
|
type: "preset",
|
|
preset: "claude_code",
|
|
};
|
|
}
|
|
|
|
// Build SDK options - use merged tools from both direct options and claudeArgs
|
|
const sdkOptions: SdkOptions = {
|
|
// Direct options from ClaudeOptions inputs
|
|
model: options.model,
|
|
maxTurns: options.maxTurns ? parseInt(options.maxTurns, 10) : undefined,
|
|
allowedTools:
|
|
mergedAllowedTools.length > 0 ? mergedAllowedTools : undefined,
|
|
disallowedTools:
|
|
mergedDisallowedTools.length > 0 ? mergedDisallowedTools : undefined,
|
|
systemPrompt,
|
|
fallbackModel: options.fallbackModel,
|
|
pathToClaudeCodeExecutable: options.pathToClaudeCodeExecutable,
|
|
|
|
// Pass through claudeArgs as extraArgs - CLI handles --mcp-config, --json-schema, etc.
|
|
// Note: allowedTools and disallowedTools have been removed from extraArgs to prevent duplicates
|
|
extraArgs,
|
|
env,
|
|
|
|
// Load settings from all sources to pick up CLI-installed plugins, CLAUDE.md, etc.
|
|
settingSources: ["user", "project", "local"],
|
|
};
|
|
|
|
return {
|
|
sdkOptions,
|
|
showFullOutput,
|
|
hasJsonSchema,
|
|
};
|
|
}
|