Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 69 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,63 +798,75 @@ Options are:

<summary>Projects</summary>

- **add_project_item** - Add project item
- `item_id`: The numeric ID of the issue or pull request to add to the project. (number, required)
- `item_type`: The item's type, either issue or pull_request. (string, required)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number. (number, required)

- **delete_project_item** - Delete project item
- `item_id`: The internal project item ID to delete from the project (not the issue or pull request ID). (number, required)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number. (number, required)

- **get_project** - Get project
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number (number, required)

- **get_project_field** - Get project field
- `field_id`: The field's id. (number, required)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number. (number, required)

- **get_project_item** - Get project item
- `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional)
- `item_id`: The item's ID. (number, required)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number. (number, required)

- **list_project_fields** - List project fields
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `per_page`: Number of results per page (max 100, default: 30) (number, optional)
- `project_number`: The project's number. (number, required)

- **list_project_items** - List project items
- `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `per_page`: Number of results per page (max 100, default: 30) (number, optional)
- `project_number`: The project's number. (number, required)
- `query`: Search query to filter items (string, optional)

- **list_projects** - List projects
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `per_page`: Number of results per page (max 100, default: 30) (number, optional)
- `query`: Filter projects by a search query (matches title and description) (string, optional)

- **update_project_item** - Update project item
- `item_id`: The unique identifier of the project item. This is not the issue or pull request ID. (number, required)
- `owner`: If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive. (string, required)
- `owner_type`: Owner type (string, required)
- `project_number`: The project's number. (number, required)
- `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {"id": 123456, "value": "New Value"} (object, required)
- **project_read** - Read project information
- `after`: Forward pagination cursor. Use when the previous response's pageInfo.hasNextPage=true. Supply pageInfo.nextCursor as 'after' and immediately request the next page. LOOP UNTIL pageInfo.hasNextPage=false (don't stop early). Keep query, fields, and per_page identical for every page. (string, optional)
- `before`: Backward pagination cursor (rare): supply to move to the preceding page using pageInfo.prevCursor. Not needed for normal forward iteration. (string, optional)
- `field_id`: Field ID (required for get_project_field) (number, optional)
- `fields`: Field IDs to include (e.g. ["102589", "985201"]). CRITICAL: Always provide to get field values. Without this, only titles returned. Get IDs from list_project_fields first. (string[], optional)
- `item_id`: Item ID (required for get_project_item) (number, optional)
- `method`: Read operation: get_project, list_projects, get_project_field, list_project_fields (call FIRST for IDs), get_project_item, list_project_items (use query + fields) (string, required)
- `owner`: GitHub username or org name (case-insensitive) (string, required)
- `owner_type`: Owner type: 'user' or 'org' (string, required)
- `per_page`: Results per page (max 50). Keep constant across paginated requests; changing mid-sequence can complicate page traversal. (number, optional)
- `project_number`: Project number (required for most methods) (number, optional)
- `query`: Query string (used ONLY with list_projects and list_project_items).

Pattern Split:

1. list_projects (project metadata only):
Scope: title text + open/closed state.
PERMITTED qualifiers: is:open, is:closed (state), simple title terms.
FORBIDDEN: is:issue, is:pr, assignee:, label:, status:, sprint-name:, parent-issue:, team-name:, priority:, etc.
Examples:
- roadmap is:open
- is:open feature planning
Reject & switch method if user intends items.

2. list_project_items (issues / PRs inside ONE project):
MUST reflect user intent; strongly prefer explicit content type if narrowed:
- "open issues" → state:open is:issue
- "merged PRs" → state:merged is:pr
- "items updated this week" → updated:>@today-7d (omit type only if mixed desired)
- "list all P1 priority items" → priority:p1 (omit state if user wants all, omit type if user speciifies "items")
- "list all open P2 issues" → is:issue state:open priority:p2 (include state if user wants open or closed, include type if user speciifies "issues" or "PRs")
Query Construction Heuristics:
a. Extract type nouns: issues → is:issue | PRs, Pulls, or Pull Requests → is:pr | tasks/tickets → is:issue (ask if ambiguity)
b. Map temporal phrases: "this week" → updated:>@today-7d
c. Map negations: "excluding wontfix" → -label:wontfix
d. Map priority adjectives: "high/sev1/p1" → priority:high OR priority:p1 (choose based on field presence)

Syntax Essentials (items):
AND: space-separated. (label:bug priority:high).
OR: comma inside one qualifier (label:bug,critical).
NOT: leading '-' (-label:wontfix).
Hyphenate multi-word field names. (team-name:"Backend Team", story-points:>5).
Quote multi-word values. (status:"In Review" team-name:"Backend Team").
Ranges: points:1..3, updated:<@today-30d.
Wildcards: title:*crash*, label:bug*.

Common Qualifier Glossary (items):
is:issue | is:pr | state:open|closed|merged | assignee:@me|username | label:NAME | status:VALUE |
priority:p1|high | sprint-name:@current | team-name:"Backend Team" | parent-issue:"org/repo#123" |
updated:>@today-7d | title:*text* | -label:wontfix | label:bug,critical | no:assignee | has:label

Pagination Mandate:
Do not analyze until ALL pages fetched (loop while pageInfo.hasNextPage=true). Always reuse identical query, fields, per_page.

Recovery Guidance:
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.

Never:
- Infer field IDs; fetch via list_project_fields.
- Drop 'fields' param on subsequent pages if field values are needed. (string, optional)

- **project_write** - Modify project items
- `item_id`: For add: issue/PR ID. For update/delete: project item ID (not issue/PR ID) (number, required)
- `item_type`: Type to add: 'issue' or 'pull_request' (required for add_project_item) (string, optional)
- `method`: Write operation: add_project_item (needs item_type, item_id), update_project_item (needs item_id, updated_field), delete_project_item (needs item_id) (string, required)
- `owner`: GitHub username or org name (case-insensitive) (string, required)
- `owner_type`: Owner type: 'user' or 'org' (string, required)
- `project_number`: Project number (number, required)
- `updated_field`: Field update object (required for update_project_item). Format: {"id": 123456, "value": <value>}. Value types: text=string, single-select=option ID (number), date=ISO string, number=number. Set value to null to clear. (object, optional)

</details>

Expand Down
77 changes: 77 additions & 0 deletions pkg/github/__toolsnaps__/project_read.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"annotations": {
"title": "Read project information",
"readOnlyHint": true
},
"description": "GitHub Projects V2 read operations.\n\nMethods: get_project | list_projects | list_project_fields | get_project_field | list_project_items | get_project_item\n\nKey distinctions:\n- list_projects: ONLY project metadata (title, open/closed). Never use item filters.\n- list_project_items: Issues/PRs inside ONE project. Prefer explicit is:issue or is:pr.\n\nField usage:\n- Call list_project_fields first to get IDs/types.\n- Use EXACT returned field names (case-insensitive match). Don't invent names or IDs.\n- Iteration synonyms (sprint/cycle/iteration) only if that field exists; map to the actual name (e.g. sprint:@current).\n- Only include filters for fields that exist and are relevant.\n\nItem query syntax:\nAND = space | OR = comma (label:bug,critical) | NOT = prefix - ( -label:wontfix )\nQuote multi-word values: status:\"In Review\" team-name:\"Backend Team\"\nHyphenate multi-word field names (story-points).\nRanges: points:1..3 dates:2025-01-01..2025-12-31\nComparisons: updated:\u003e@today-7d priority:\u003e1 points:\u003c=10\nWildcards: title:*crash* label:bug*\nTemporal shortcuts: @today @today-7d @today-30d\nIteration shortcuts: @current @next @previous\n\nPagination (mandatory):\nLoop while pageInfo.hasNextPage=true using after=nextCursor. Keep query, fields, per_page IDENTICAL each page.\n\nFields parameter:\nInclude field IDs on EVERY paginated list_project_items call if you need values. Omit → title only.\n\nCounting rules:\n- Count items array length after full pagination.\n- If multi-page: collect all pages, dedupe by item.id (fallback node_id) before totals.\n- Never count field objects, content, or nested arrays as separate items.\n- item.id = project item ID (for updates/deletes). item.content.id = underlying issue/PR ID.\n\nSummary vs list:\n- Summaries ONLY if user uses verbs: analyze | summarize | summary | report | overview | insights.\n- Listing verbs (list/show/get/fetch/display/enumerate) → just enumerate + total.\n\nExamples:\nlist_projects: \"roadmap is:open\"\nlist_project_items: state:open is:issue sprint:@current priority:high updated:\u003e@today-7d\n\nSelf-check before returning:\n☑ Paginated fully ☑ Dedupe by id/node_id ☑ Correct IDs used ☑ Field names valid ☑ Summary only if requested.\n\nReturn COMPLETE data or state what's missing (e.g. pages skipped).",
"inputSchema": {
"properties": {
"after": {
"description": "Forward pagination cursor. Use when the previous response's pageInfo.hasNextPage=true. Supply pageInfo.nextCursor as 'after' and immediately request the next page. LOOP UNTIL pageInfo.hasNextPage=false (don't stop early). Keep query, fields, and per_page identical for every page.",
"type": "string"
},
"before": {
"description": "Backward pagination cursor (rare): supply to move to the preceding page using pageInfo.prevCursor. Not needed for normal forward iteration.",
"type": "string"
},
"field_id": {
"description": "Field ID (required for get_project_field)",
"type": "number"
},
"fields": {
"description": "Field IDs to include (e.g. [\"102589\", \"985201\"]). CRITICAL: Always provide to get field values. Without this, only titles returned. Get IDs from list_project_fields first.",
"items": {
"type": "string"
},
"type": "array"
},
"item_id": {
"description": "Item ID (required for get_project_item)",
"type": "number"
},
"method": {
"description": "Read operation: get_project, list_projects, get_project_field, list_project_fields (call FIRST for IDs), get_project_item, list_project_items (use query + fields)",
"enum": [
"get_project",
"list_projects",
"get_project_field",
"list_project_fields",
"get_project_item",
"list_project_items"
],
"type": "string"
},
"owner": {
"description": "GitHub username or org name (case-insensitive)",
"type": "string"
},
"owner_type": {
"description": "Owner type: 'user' or 'org'",
"enum": [
"user",
"org"
],
"type": "string"
},
"per_page": {
"description": "Results per page (max 50). Keep constant across paginated requests; changing mid-sequence can complicate page traversal.",
"type": "number"
},
"project_number": {
"description": "Project number (required for most methods)",
"type": "number"
},
"query": {
"description": "Query string (used ONLY with list_projects and list_project_items). \n\nPattern Split:\n\n1. list_projects (project metadata only):\n Scope: title text + open/closed state.\n PERMITTED qualifiers: is:open, is:closed (state), simple title terms.\n FORBIDDEN: is:issue, is:pr, assignee:, label:, status:, sprint-name:, parent-issue:, team-name:, priority:, etc.\n Examples:\n - roadmap is:open\n - is:open feature planning\n Reject \u0026 switch method if user intends items.\n\n2. list_project_items (issues / PRs inside ONE project):\n MUST reflect user intent; strongly prefer explicit content type if narrowed:\n - \"open issues\" → state:open is:issue\n - \"merged PRs\" → state:merged is:pr\n - \"items updated this week\" → updated:\u003e@today-7d (omit type only if mixed desired)\n - \"list all P1 priority items\" → priority:p1 (omit state if user wants all, omit type if user speciifies \"items\")\n - \"list all open P2 issues\" → is:issue state:open priority:p2 (include state if user wants open or closed, include type if user speciifies \"issues\" or \"PRs\")\n Query Construction Heuristics:\n a. Extract type nouns: issues → is:issue | PRs, Pulls, or Pull Requests → is:pr | tasks/tickets → is:issue (ask if ambiguity)\n b. Map temporal phrases: \"this week\" → updated:\u003e@today-7d\n c. Map negations: \"excluding wontfix\" → -label:wontfix\n d. 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\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.",
"type": "string"
}
},
"required": [
"method",
"owner_type",
"owner"
],
"type": "object"
},
"name": "project_read"
}
62 changes: 62 additions & 0 deletions pkg/github/__toolsnaps__/project_write.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"annotations": {
"title": "Modify project items",
"readOnlyHint": false
},
"description": "Write operations for GitHub Projects.\n\nMethods: add_project_item (add issue/PR), update_project_item (update fields), delete_project_item (remove item).\nNote: item_id for add is the issue/PR ID; for update/delete it's the project item ID.",
"inputSchema": {
"properties": {
"item_id": {
"description": "For add: issue/PR ID. For update/delete: project item ID (not issue/PR ID)",
"type": "number"
},
"item_type": {
"description": "Type to add: 'issue' or 'pull_request' (required for add_project_item)",
"enum": [
"issue",
"pull_request"
],
"type": "string"
},
"method": {
"description": "Write operation: add_project_item (needs item_type, item_id), update_project_item (needs item_id, updated_field), delete_project_item (needs item_id)",
"enum": [
"add_project_item",
"update_project_item",
"delete_project_item"
],
"type": "string"
},
"owner": {
"description": "GitHub username or org name (case-insensitive)",
"type": "string"
},
"owner_type": {
"description": "Owner type: 'user' or 'org'",
"enum": [
"user",
"org"
],
"type": "string"
},
"project_number": {
"description": "Project number",
"type": "number"
},
"updated_field": {
"description": "Field update object (required for update_project_item). Format: {\"id\": 123456, \"value\": \u003cvalue\u003e}. Value types: text=string, single-select=option ID (number), date=ISO string, number=number. Set value to null to clear.",
"properties": {},
"type": "object"
}
},
"required": [
"method",
"owner_type",
"owner",
"project_number",
"item_id"
],
"type": "object"
},
"name": "project_write"
}
Loading
Loading