@@ -320,6 +320,11 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
320320 "State the concrete signal (e.g. 'Reports a crash when saving' → bug)." ,
321321 MaxLength : jsonschema .Ptr (280 ),
322322 },
323+ "is_suggestion" : {
324+ Type : "boolean" ,
325+ Description : "If true, propose this label instead of applying it. " +
326+ "Labels not marked as suggestions are applied to the issue; suggested labels are returned as proposals in the response." ,
327+ },
323328 },
324329 Required : []string {"name" },
325330 },
@@ -364,6 +369,7 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
364369
365370 anyRationale := false
366371 payload := make ([]any , 0 , len (labelsSlice ))
372+ suggested := make ([]any , 0 )
367373 for _ , item := range labelsSlice {
368374 switch v := item .(type ) {
369375 case string :
@@ -381,6 +387,14 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
381387 if len ([]rune (rationale )) > 280 {
382388 return utils .NewToolResultError ("label rationale must be 280 characters or less" ), nil , nil
383389 }
390+ isSuggestion , err := OptionalParam [bool ](v , "is_suggestion" )
391+ if err != nil {
392+ return utils .NewToolResultError (err .Error ()), nil , nil
393+ }
394+ if isSuggestion {
395+ suggested = append (suggested , labelWithRationale {Name : name , Rationale : rationale })
396+ continue
397+ }
384398 if rationale == "" {
385399 payload = append (payload , name )
386400 } else {
@@ -392,6 +406,21 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
392406 }
393407 }
394408
409+ // If no labels are to be applied, only return suggestions without
410+ // calling the API.
411+ if len (payload ) == 0 {
412+ r , err := json .Marshal (map [string ]any {
413+ "owner" : owner ,
414+ "repo" : repo ,
415+ "issue_number" : issueNumber ,
416+ "suggested_labels" : suggested ,
417+ })
418+ if err != nil {
419+ return utils .NewToolResultErrorFromErr ("failed to marshal suggestion" , err ), nil , nil
420+ }
421+ return utils .NewToolResultText (string (r )), nil , nil
422+ }
423+
395424 client , err := deps .GetClient (ctx )
396425 if err != nil {
397426 return utils .NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
@@ -422,10 +451,14 @@ func GranularUpdateIssueLabels(t translations.TranslationHelperFunc) inventory.S
422451 }
423452 defer func () { _ = resp .Body .Close () }()
424453
425- r , err := json .Marshal (MinimalResponse {
426- ID : fmt .Sprintf ("%d" , issue .GetID ()),
427- URL : issue .GetHTMLURL (),
428- })
454+ respBody := map [string ]any {
455+ "id" : fmt .Sprintf ("%d" , issue .GetID ()),
456+ "url" : issue .GetHTMLURL (),
457+ }
458+ if len (suggested ) > 0 {
459+ respBody ["suggested_labels" ] = suggested
460+ }
461+ r , err := json .Marshal (respBody )
429462 if err != nil {
430463 return utils .NewToolResultErrorFromErr ("failed to marshal response" , err ), nil , nil
431464 }
@@ -961,6 +994,11 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
961994 "State the concrete signal (e.g. 'Reports a crash when saving' → high priority)." ,
962995 MaxLength : jsonschema .Ptr (280 ),
963996 },
997+ "is_suggestion" : {
998+ Type : "boolean" ,
999+ Description : "If true, propose this field value instead of applying it. " +
1000+ "Fields not marked as suggestions are applied to the issue; suggested fields are returned as proposals in the response." ,
1001+ },
9641002 },
9651003 Required : []string {"field_id" },
9661004 },
@@ -1010,6 +1048,7 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
10101048 }
10111049
10121050 issueFields := make ([]IssueFieldCreateOrUpdateInput , 0 , len (fieldMaps ))
1051+ suggestedFields := make ([]map [string ]any , 0 )
10131052 for _ , fieldMap := range fieldMaps {
10141053 fieldID , err := RequiredParam [string ](fieldMap , "field_id" )
10151054 if err != nil {
@@ -1019,35 +1058,41 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
10191058 input := IssueFieldCreateOrUpdateInput {
10201059 FieldID : githubv4 .ID (fieldID ),
10211060 }
1061+ display := map [string ]any {"field_id" : fieldID }
10221062
10231063 // Count how many value keys are present; exactly one is required.
10241064 valueCount := 0
10251065
10261066 if v , err := OptionalParam [string ](fieldMap , "text_value" ); err == nil && v != "" {
10271067 input .TextValue = githubv4 .NewString (githubv4 .String (v ))
1068+ display ["text_value" ] = v
10281069 valueCount ++
10291070 }
10301071 if v , err := OptionalParam [float64 ](fieldMap , "number_value" ); err == nil {
10311072 if _ , exists := fieldMap ["number_value" ]; exists {
10321073 gqlFloat := githubv4 .Float (v )
10331074 input .NumberValue = & gqlFloat
1075+ display ["number_value" ] = v
10341076 valueCount ++
10351077 }
10361078 }
10371079 if v , err := OptionalParam [string ](fieldMap , "date_value" ); err == nil && v != "" {
10381080 input .DateValue = githubv4 .NewString (githubv4 .String (v ))
1081+ display ["date_value" ] = v
10391082 valueCount ++
10401083 }
10411084 if v , err := OptionalParam [string ](fieldMap , "single_select_option_id" ); err == nil && v != "" {
10421085 optionID := githubv4 .ID (v )
10431086 input .SingleSelectOptionID = & optionID
1087+ display ["single_select_option_id" ] = v
10441088 valueCount ++
10451089 }
10461090 if _ , exists := fieldMap ["delete" ]; exists {
10471091 del , err := OptionalParam [bool ](fieldMap , "delete" )
10481092 if err == nil && del {
10491093 deleteVal := githubv4 .Boolean (true )
10501094 input .Delete = & deleteVal
1095+ display ["delete" ] = true
10511096 valueCount ++
10521097 }
10531098 }
@@ -1070,12 +1115,37 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
10701115 }
10711116 if rationale != "" {
10721117 input .Rationale = githubv4 .NewString (githubv4 .String (rationale ))
1118+ display ["rationale" ] = rationale
10731119 }
10741120 }
10751121
1122+ isSuggestion , err := OptionalParam [bool ](fieldMap , "is_suggestion" )
1123+ if err != nil {
1124+ return utils .NewToolResultError (err .Error ()), nil , nil
1125+ }
1126+ if isSuggestion {
1127+ suggestedFields = append (suggestedFields , display )
1128+ continue
1129+ }
1130+
10761131 issueFields = append (issueFields , input )
10771132 }
10781133
1134+ // If no fields are to be applied, only return suggestions without
1135+ // calling the API.
1136+ if len (issueFields ) == 0 {
1137+ r , err := json .Marshal (map [string ]any {
1138+ "owner" : owner ,
1139+ "repo" : repo ,
1140+ "issue_number" : issueNumber ,
1141+ "suggested_fields" : suggestedFields ,
1142+ })
1143+ if err != nil {
1144+ return utils .NewToolResultErrorFromErr ("failed to marshal suggestion" , err ), nil , nil
1145+ }
1146+ return utils .NewToolResultText (string (r )), nil , nil
1147+ }
1148+
10791149 gqlClient , err := deps .GetGQLClient (ctx )
10801150 if err != nil {
10811151 return utils .NewToolResultErrorFromErr ("failed to get GitHub GraphQL client" , err ), nil , nil
@@ -1121,10 +1191,14 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
11211191 return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "failed to set issue field values" , err ), nil , nil
11221192 }
11231193
1124- r , err := json .Marshal (MinimalResponse {
1125- ID : fmt .Sprintf ("%v" , mutation .SetIssueFieldValue .Issue .ID ),
1126- URL : string (mutation .SetIssueFieldValue .Issue .URL ),
1127- })
1194+ respBody := map [string ]any {
1195+ "id" : fmt .Sprintf ("%v" , mutation .SetIssueFieldValue .Issue .ID ),
1196+ "url" : string (mutation .SetIssueFieldValue .Issue .URL ),
1197+ }
1198+ if len (suggestedFields ) > 0 {
1199+ respBody ["suggested_fields" ] = suggestedFields
1200+ }
1201+ r , err := json .Marshal (respBody )
11281202 if err != nil {
11291203 return utils .NewToolResultErrorFromErr ("failed to marshal response" , err ), nil , nil
11301204 }
0 commit comments