Skip to content

Commit 63bbdf0

Browse files
committed
final
1 parent cd6d1a2 commit 63bbdf0

File tree

4 files changed

+17
-104
lines changed

4 files changed

+17
-104
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,8 @@ Query Construction Heuristics:
860860
b. Map temporal phrases: "this week" → updated:>@today-7d
861861
c. Map negations: "excluding wontfix" → -label:wontfix
862862
d. Map priority adjectives: "high/sev1/p1" → priority:high OR priority:p1 (choose based on field presence)
863+
e. When filtering by label, always use wildcard matching to account for cross-repository differences or emojis: (e.g. "bug 🐛" → label:*bug*)
864+
f. When filtering by milestone, always use wildcard matching to account for cross-repository differences: (e.g. "v1.0" → milestone:*v1.0*)
863865

864866
Syntax Essentials (items):
865867
AND: space-separated. (label:bug priority:high).

pkg/github/__toolsnaps__/list_project_items.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"type": "number"
4343
},
4444
"query": {
45-
"description": "Query string - For advanced filtering of project items using GitHub's search syntax:\n\nMUST reflect user intent; strongly prefer explicit content type if narrowed:\n\t- \"open issues\" → state:open is:issue\n\t- \"merged PRs\" → state:merged is:pr\n\t- \"items updated this week\" → updated:\u003e@today-7d (omit type only if mixed desired)\n\t- \"list all P1 priority items\" → priority:p1 (omit state if user wants all, omit type if user specifies \"items\")\n\t- \"list all open P2 issues\" → is:issue state:open priority:p2 (include state if user wants open or closed, include type if user specifies \"issues\" or \"PRs\")\n\t- \"all open issues I'm working on\" → is:issue state:open assignee:@me\n\nQuery Construction Heuristics:\n\ta. Extract type nouns: issues → is:issue | PRs, Pulls, or Pull Requests → is:pr | tasks/tickets → is:issue (ask if ambiguity)\n\tb. Map temporal phrases: \"this week\" → updated:\u003e@today-7d\n\tc. Map negations: \"excluding wontfix\" → -label:wontfix\n\td. Map priority adjectives: \"high/sev1/p1\" → priority:high OR priority:p1 (choose based on field presence)\n\nSyntax Essentials (items):\n AND: space-separated. (label:bug priority:high).\n OR: comma inside one qualifier (label:bug,critical).\n NOT: leading '-' (-label:wontfix).\n Hyphenate multi-word field names. (team-name:\"Backend Team\", story-points:\u003e5).\n Quote multi-word values. (status:\"In Review\" team-name:\"Backend Team\").\n Ranges: points:1..3, updated:\u003c@today-30d.\n Wildcards: title:*crash*, label:bug*.\n\t Assigned to User: assignee:@me | assignee:username | no:assignee\n\nCommon Qualifier Glossary (items):\n is:issue | is:pr | state:open|closed|merged | assignee:@me|username | label:NAME | status:VALUE |\n priority:p1|high | sprint-name:@current | team-name:\"Backend Team\" | parent-issue:\"org/repo#123\" |\n updated:\u003e@today-7d | title:*text* | -label:wontfix | label:bug,critical | no:assignee | has:label\n\nPagination Mandate:\n Do not analyze until ALL pages fetched (loop while pageInfo.hasNextPage=true). Always reuse identical query, fields, per_page.\n\nRecovery Guidance:\n If user provides ambiguous request (\"show project activity\") → ask clarification OR return mixed set (omit is:issue/is:pr). If user mixes project + item qualifiers in one phrase → split: run list_projects for discovery, then list_project_items for detail.\n\nNever:\n - Infer field IDs; fetch via list_project_fields.\n - Drop 'fields' param on subsequent pages if field values are needed.",
45+
"description": "Query string - For advanced filtering of project items using GitHub's search syntax:\n\nMUST reflect user intent; strongly prefer explicit content type if narrowed:\n\t- \"open issues\" → state:open is:issue\n\t- \"merged PRs\" → state:merged is:pr\n\t- \"items updated this week\" → updated:\u003e@today-7d (omit type only if mixed desired)\n\t- \"list all P1 priority items\" → priority:p1 (omit state if user wants all, omit type if user specifies \"items\")\n\t- \"list all open P2 issues\" → is:issue state:open priority:p2 (include state if user wants open or closed, include type if user specifies \"issues\" or \"PRs\")\n\t- \"all open issues I'm working on\" → is:issue state:open assignee:@me\n\nQuery Construction Heuristics:\n\ta. Extract type nouns: issues → is:issue | PRs, Pulls, or Pull Requests → is:pr | tasks/tickets → is:issue (ask if ambiguity)\n\tb. Map temporal phrases: \"this week\" → updated:\u003e@today-7d\n\tc. Map negations: \"excluding wontfix\" → -label:wontfix\n\td. Map priority adjectives: \"high/sev1/p1\" → priority:high OR priority:p1 (choose based on field presence)\n\te. When filtering by label, always use wildcard matching to account for cross-repository differences or emojis: (e.g. \"bug 🐛\" → label:*bug*)\n\tf. When filtering by milestone, always use wildcard matching to account for cross-repository differences: (e.g. \"v1.0\" → milestone:*v1.0*)\n\nSyntax Essentials (items):\n AND: space-separated. (label:bug priority:high).\n OR: comma inside one qualifier (label:bug,critical).\n NOT: leading '-' (-label:wontfix).\n Hyphenate multi-word field names. (team-name:\"Backend Team\", story-points:\u003e5).\n Quote multi-word values. (status:\"In Review\" team-name:\"Backend Team\").\n Ranges: points:1..3, updated:\u003c@today-30d.\n Wildcards: title:*crash*, label:bug*.\n\t Assigned to User: assignee:@me | assignee:username | no:assignee\n\nCommon Qualifier Glossary (items):\n is:issue | is:pr | state:open|closed|merged | assignee:@me|username | label:NAME | status:VALUE |\n priority:p1|high | sprint-name:@current | team-name:\"Backend Team\" | parent-issue:\"org/repo#123\" |\n updated:\u003e@today-7d | title:*text* | -label:wontfix | label:bug,critical | no:assignee | has:label\n\nPagination Mandate:\n Do not analyze until ALL pages fetched (loop while pageInfo.hasNextPage=true). Always reuse identical query, fields, per_page.\n\nRecovery Guidance:\n If user provides ambiguous request (\"show project activity\") → ask clarification OR return mixed set (omit is:issue/is:pr). If user mixes project + item qualifiers in one phrase → split: run list_projects for discovery, then list_project_items for detail.\n\nNever:\n - Infer field IDs; fetch via list_project_fields.\n - Drop 'fields' param on subsequent pages if field values are needed.",
4646
"type": "string"
4747
}
4848
},

pkg/github/instructions.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,14 @@ Use 'list_discussion_categories' to understand available categories before creat
6565
case "projects":
6666
return `## Projects
6767
68-
When using 'list_project_items', follow the these guidelines:
68+
When using 'list_project_items', follow these guidelines:
6969
7070
Field usage:
7171
- Call list_project_fields first to understand available fields and get IDs/types before filtering.
7272
- Use EXACT returned field names (case-insensitive match). Don't invent names or IDs.
7373
- Iteration synonyms (sprint/cycle/iteration) only if that field exists; map to the actual name (e.g. sprint:@current).
7474
- Only include filters for fields that exist and are relevant.
7575
76-
Item query syntax:
77-
- AND = space | OR = comma (label:bug,critical) | NOT = prefix - ( -label:wontfix )
78-
- Quote multi-word values: status:"In Review" team-name:"Backend Team"
79-
- Hyphenate multi-word field names (story-points).
80-
- Ranges: points:1..3 dates:2025-01-01..2025-12-31
81-
- Comparisons: updated:>@today-7d priority:>1 points:<=10
82-
- Wildcards: title:*crash* label:bug*
83-
- Temporal shortcuts: @today @today-7d @today-30d
84-
- Iteration shortcuts: @current @next @previous
85-
8676
Pagination (mandatory):
8777
- Loop while pageInfo.hasNextPage=true using after=nextCursor. Keep query, fields, per_page IDENTICAL each page.
8878

pkg/github/projects.go

Lines changed: 13 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,8 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
291291
}
292292
defer func() { _ = resp.Body.Close() }()
293293

294-
filteredFields := filterSpecialTypes(projectFields)
295-
296294
response := map[string]any{
297-
"fields": filteredFields,
295+
"fields": projectFields,
298296
"pageInfo": buildPageInfo(resp),
299297
}
300298

@@ -425,6 +423,8 @@ Query Construction Heuristics:
425423
b. Map temporal phrases: "this week" → updated:>@today-7d
426424
c. Map negations: "excluding wontfix" → -label:wontfix
427425
d. Map priority adjectives: "high/sev1/p1" → priority:high OR priority:p1 (choose based on field presence)
426+
e. When filtering by label, always use wildcard matching to account for cross-repository differences or emojis: (e.g. "bug 🐛" → label:*bug*)
427+
f. When filtering by milestone, always use wildcard matching to account for cross-repository differences: (e.g. "v1.0" → milestone:*v1.0*)
428428
429429
Syntax Essentials (items):
430430
AND: space-separated. (label:bug priority:high).
@@ -535,14 +535,6 @@ Never:
535535
}
536536
defer func() { _ = resp.Body.Close() }()
537537

538-
if len(projectItems) > 0 {
539-
for i := range projectItems {
540-
if len(projectItems[i].Fields) > 0 {
541-
projectItems[i].Fields = filterSpecialTypes(projectItems[i].Fields)
542-
}
543-
}
544-
}
545-
546538
response := map[string]any{
547539
"items": projectItems,
548540
"pageInfo": buildPageInfo(resp),
@@ -651,10 +643,6 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
651643
}
652644
defer func() { _ = resp.Body.Close() }()
653645

654-
if len(projectItem.Fields) > 0 {
655-
projectItem.Fields = filterSpecialTypes(projectItem.Fields)
656-
}
657-
658646
r, err := json.Marshal(projectItem)
659647
if err != nil {
660648
return nil, fmt.Errorf("failed to marshal response: %w", err)
@@ -976,27 +964,13 @@ type projectV2Field struct {
976964
UpdatedAt *github.Timestamp `json:"updated_at,omitempty"`
977965
}
978966

979-
func (f *projectV2Field) getDataType() string {
980-
if f == nil {
981-
return ""
982-
}
983-
return strings.ToLower(f.DataType)
984-
}
985-
986967
type projectV2ItemFieldValue struct {
987968
ID *int64 `json:"id,omitempty"`
988969
Name string `json:"name,omitempty"`
989970
DataType string `json:"data_type,omitempty"`
990971
Value any `json:"value,omitempty"`
991972
}
992973

993-
func (v *projectV2ItemFieldValue) getDataType() string {
994-
if v == nil {
995-
return ""
996-
}
997-
return strings.ToLower(v.DataType)
998-
}
999-
1000974
type projectV2Item struct {
1001975
ArchivedAt *github.Timestamp `json:"archived_at,omitempty"`
1002976
Content *projectV2ItemContent `json:"content,omitempty"`
@@ -1014,29 +988,16 @@ type projectV2Item struct {
1014988
}
1015989

1016990
type projectV2ItemContent struct {
1017-
Body *string `json:"body,omitempty"`
1018-
ClosedAt *github.Timestamp `json:"closed_at,omitempty"`
1019-
CreatedAt *github.Timestamp `json:"created_at,omitempty"`
1020-
ID *int64 `json:"id,omitempty"`
1021-
Number *int `json:"number,omitempty"`
1022-
Repository *projectV2ItemContentRepository `json:"repository,omitempty"`
1023-
State *string `json:"state,omitempty"`
1024-
StateReason *string `json:"stateReason,omitempty"`
1025-
Title *string `json:"title,omitempty"`
1026-
UpdatedAt *github.Timestamp `json:"updated_at,omitempty"`
1027-
URL *string `json:"url,omitempty"`
1028-
Type *any `json:"type,omitempty"`
1029-
Labels []*any `json:"labels,omitempty"`
1030-
Assignees []*MinimalUser `json:"assignees,omitempty"`
1031-
Milestone *any `json:"milestone,omitempty"`
1032-
}
1033-
1034-
type projectV2ItemContentRepository struct {
1035-
ID *int64 `json:"id"`
1036-
Name *string `json:"name"`
1037-
FullName *string `json:"full_name"`
1038-
Description *string `json:"description,omitempty"`
1039-
HTMLURL *string `json:"html_url"`
991+
Body *string `json:"body,omitempty"`
992+
ClosedAt *github.Timestamp `json:"closed_at,omitempty"`
993+
CreatedAt *github.Timestamp `json:"created_at,omitempty"`
994+
ID *int64 `json:"id,omitempty"`
995+
Number *int `json:"number,omitempty"`
996+
State *string `json:"state,omitempty"`
997+
StateReason *string `json:"stateReason,omitempty"`
998+
Title *string `json:"title,omitempty"`
999+
UpdatedAt *github.Timestamp `json:"updated_at,omitempty"`
1000+
URL *string `json:"url,omitempty"`
10401001
}
10411002

10421003
type pageInfo struct {
@@ -1136,46 +1097,6 @@ func extractPaginationOptions(request mcp.CallToolRequest) (paginationOptions, e
11361097
}, nil
11371098
}
11381099

1139-
// "special" data types that are present in the project item's content object.
1140-
var specialFieldDataTypes = map[string]struct{}{
1141-
"assignees": {},
1142-
"labels": {},
1143-
"linked_pull_requests": {},
1144-
"milestone": {},
1145-
"parent_issue": {},
1146-
"repository": {},
1147-
"reviewers": {},
1148-
"sub_issues_progress": {},
1149-
"title": {},
1150-
}
1151-
1152-
// filterSpecialTypes returns a new slice containing only those field definitions
1153-
// or field values whose DataType is NOT in the specialFieldDataTypes set. The
1154-
// input must be a slice whose element type implements getDataType() string.
1155-
//
1156-
// Applicable to:
1157-
//
1158-
// []*projectV2Field
1159-
// []*projectV2ItemFieldValue
1160-
//
1161-
// Example:
1162-
//
1163-
// filtered := filterSpecialTypes(fields)
1164-
func filterSpecialTypes[T interface{ getDataType() string }](fields []T) []T {
1165-
if len(fields) == 0 {
1166-
return fields
1167-
}
1168-
out := make([]T, 0, len(fields))
1169-
for _, f := range fields {
1170-
dt := f.getDataType()
1171-
if _, isSpecial := specialFieldDataTypes[dt]; isSpecial {
1172-
continue
1173-
}
1174-
out = append(out, f)
1175-
}
1176-
return out
1177-
}
1178-
11791100
// addOptions adds the parameters in opts as URL query parameters to s. opts
11801101
// must be a struct whose fields may contain "url" tags.
11811102
func addOptions(s string, opts any) (string, error) {

0 commit comments

Comments
 (0)