diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index b5040a2d38e..3a61e88d166 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -5074,6 +5074,11 @@ "additionalProperties": true } ] + }, + "normalize-closing-keywords": { + "type": "boolean", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", + "examples": [true, false] } }, "additionalProperties": false, @@ -6400,6 +6405,11 @@ "additionalProperties": true } ] + }, + "normalize-closing-keywords": { + "type": "boolean", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", + "examples": [true, false] } }, "additionalProperties": false, @@ -6796,6 +6806,11 @@ "description": "When true, adds workflows: write to the GitHub App token permissions. Required when allowed-files targets .github/workflows/ paths. Requires safe-outputs.github-app to be configured because the workflows permission is a GitHub App-only permission and cannot be granted via GITHUB_TOKEN.", "default": false, "examples": [true, false] + }, + "normalize-closing-keywords": { + "type": "boolean", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", + "examples": [true, false] } }, "additionalProperties": false, diff --git a/pkg/workflow/tool_description_enhancer.go b/pkg/workflow/tool_description_enhancer.go index ce7f5546c8d..fb9e2d011a2 100644 --- a/pkg/workflow/tool_description_enhancer.go +++ b/pkg/workflow/tool_description_enhancer.go @@ -71,6 +71,9 @@ func enhanceToolDescription(toolName, baseDescription string, safeOutputs *SafeO if config.RequireTemporaryID { constraints = append(constraints, "temporary_id is required.") } + if config.NormalizeClosingKeywords != nil && *config.NormalizeClosingKeywords { + constraints = append(constraints, "Backtick-wrapped issue-closing keyword references (e.g. `Closes #1`) in the body field will be automatically normalized to plain text.") + } } case "set_issue_field": @@ -217,6 +220,9 @@ func enhanceToolDescription(toolName, baseDescription string, safeOutputs *SafeO if config.TargetRepoSlug != "" { constraints = append(constraints, fmt.Sprintf("Comments will be added in repository %q.", config.TargetRepoSlug)) } + if config.NormalizeClosingKeywords != nil && *config.NormalizeClosingKeywords { + constraints = append(constraints, "Backtick-wrapped issue-closing keyword references (e.g. `Closes #1`) in the body field will be automatically normalized to plain text.") + } } constraints = append(constraints, "Supports reply_to_id for discussion threading.") @@ -247,6 +253,9 @@ func enhanceToolDescription(toolName, baseDescription string, safeOutputs *SafeO if config.RequireTemporaryID { constraints = append(constraints, "temporary_id is required.") } + if config.NormalizeClosingKeywords != nil && *config.NormalizeClosingKeywords { + constraints = append(constraints, "Backtick-wrapped issue-closing keyword references (e.g. `Closes #1`) in the body field will be automatically normalized to plain text.") + } } case "create_pull_request_review_comment": diff --git a/pkg/workflow/tool_description_enhancer_test.go b/pkg/workflow/tool_description_enhancer_test.go index 4793cee867e..d800cf62298 100644 --- a/pkg/workflow/tool_description_enhancer_test.go +++ b/pkg/workflow/tool_description_enhancer_test.go @@ -202,3 +202,69 @@ func TestEnhanceToolDescriptionSubmitPullRequestReviewTargetRepo(t *testing.T) { t.Fatalf("expected target repo constraint in description, got: %s", description) } } + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsCreateIssue(t *testing.T) { + description := enhanceToolDescription("create_issue", "Create an issue.", &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(true)}, + }, + }) + if !strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("expected normalize-closing-keywords note in description, got: %s", description) + } +} + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsFalseCreateIssue(t *testing.T) { + description := enhanceToolDescription("create_issue", "Create an issue.", &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(false)}, + }, + }) + if strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("did not expect normalize-closing-keywords note when disabled, got: %s", description) + } +} + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsAddComment(t *testing.T) { + description := enhanceToolDescription("add_comment", "Add a comment.", &SafeOutputsConfig{ + AddComments: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(true)}, + }, + }) + if !strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("expected normalize-closing-keywords note in description, got: %s", description) + } +} + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsFalseAddComment(t *testing.T) { + description := enhanceToolDescription("add_comment", "Add a comment.", &SafeOutputsConfig{ + AddComments: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(false)}, + }, + }) + if strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("did not expect normalize-closing-keywords note when disabled, got: %s", description) + } +} + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsCreatePullRequest(t *testing.T) { + description := enhanceToolDescription("create_pull_request", "Create a pull request.", &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(true)}, + }, + }) + if !strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("expected normalize-closing-keywords note in description, got: %s", description) + } +} + +func TestEnhanceToolDescriptionNormalizeClosingKeywordsFalseCreatePullRequest(t *testing.T) { + description := enhanceToolDescription("create_pull_request", "Create a pull request.", &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{NormalizeClosingKeywords: boolPtr(false)}, + }, + }) + if strings.Contains(description, "Backtick-wrapped issue-closing keyword references") { + t.Fatalf("did not expect normalize-closing-keywords note when disabled, got: %s", description) + } +}