This is page 2 of 2. Use http://codebase.md/nguyenvanduocit/jira-mcp?page={x} to view the full context. # Directory Structure ``` ├── .claude │ └── commands │ ├── speckit.analyze.md │ ├── speckit.checklist.md │ ├── speckit.clarify.md │ ├── speckit.constitution.md │ ├── speckit.implement.md │ ├── speckit.plan.md │ ├── speckit.specify.md │ └── speckit.tasks.md ├── .github │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows │ ├── gitleaks.yaml │ ├── release.yaml │ └── scan.yaml ├── .gitignore ├── .specify │ ├── memory │ │ └── constitution.md │ ├── scripts │ │ └── bash │ │ ├── check-prerequisites.sh │ │ ├── common.sh │ │ ├── create-new-feature.sh │ │ ├── setup-plan.sh │ │ └── update-agent-context.sh │ └── templates │ ├── agent-file-template.md │ ├── checklist-template.md │ ├── plan-template.md │ ├── spec-template.md │ └── tasks-template.md ├── CHANGELOG.md ├── CLAUDE.md ├── Dockerfile ├── go.mod ├── go.sum ├── LICENSE ├── main.go ├── prompts │ └── jira_prompts.go ├── README.md ├── services │ ├── atlassian.go │ ├── httpclient.go │ └── jira.go ├── specs │ └── 001-i-want-to │ ├── api-research.md │ ├── checklists │ │ ├── implementation-readiness.md │ │ └── requirements.md │ ├── contracts │ │ └── mcp-tool-contract.json │ ├── data-model.md │ ├── plan.md │ ├── quickstart.md │ ├── research.md │ ├── spec.md │ ├── tasks.md │ ├── test-dev-api.sh │ └── test-get-build.sh ├── tools │ ├── jira_comment.go │ ├── jira_development.go │ ├── jira_history.go │ ├── jira_issue.go │ ├── jira_relationship.go │ ├── jira_search.go │ ├── jira_sprint.go │ ├── jira_status.go │ ├── jira_transition.go │ ├── jira_version.go │ └── jira_worklog.go └── util ├── jira_formatter.go └── README.md ``` # Files -------------------------------------------------------------------------------- /tools/jira_issue.go: -------------------------------------------------------------------------------- ```go package tools import ( "context" "fmt" "strings" "github.com/ctreminiom/go-atlassian/pkg/infra/models" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "github.com/nguyenvanduocit/jira-mcp/services" "github.com/nguyenvanduocit/jira-mcp/util" ) // Input types for typed tools type GetIssueInput struct { IssueKey string `json:"issue_key" validate:"required"` Fields string `json:"fields,omitempty"` Expand string `json:"expand,omitempty"` } type CreateIssueInput struct { ProjectKey string `json:"project_key" validate:"required"` Summary string `json:"summary" validate:"required"` Description string `json:"description" validate:"required"` IssueType string `json:"issue_type" validate:"required"` } type CreateChildIssueInput struct { ParentIssueKey string `json:"parent_issue_key" validate:"required"` Summary string `json:"summary" validate:"required"` Description string `json:"description" validate:"required"` IssueType string `json:"issue_type,omitempty"` } type UpdateIssueInput struct { IssueKey string `json:"issue_key" validate:"required"` Summary string `json:"summary,omitempty"` Description string `json:"description,omitempty"` } type ListIssueTypesInput struct { ProjectKey string `json:"project_key" validate:"required"` } func RegisterJiraIssueTool(s *server.MCPServer) { jiraGetIssueTool := mcp.NewTool("jira_get_issue", mcp.WithDescription("Retrieve detailed information about a specific Jira issue including its status, assignee, description, subtasks, and available transitions"), mcp.WithString("issue_key", mcp.Required(), mcp.Description("The unique identifier of the Jira issue (e.g., KP-2, PROJ-123)")), mcp.WithString("fields", mcp.Description("Comma-separated list of fields to retrieve (e.g., 'summary,status,assignee'). If not specified, all fields are returned.")), mcp.WithString("expand", mcp.Description("Comma-separated list of fields to expand for additional details (e.g., 'transitions,changelog,subtasks'). Default: 'transitions,changelog'")), ) s.AddTool(jiraGetIssueTool, mcp.NewTypedToolHandler(jiraGetIssueHandler)) jiraCreateIssueTool := mcp.NewTool("jira_create_issue", mcp.WithDescription("Create a new Jira issue with specified details. Returns the created issue's key, ID, and URL"), mcp.WithString("project_key", mcp.Required(), mcp.Description("Project identifier where the issue will be created (e.g., KP, PROJ)")), mcp.WithString("summary", mcp.Required(), mcp.Description("Brief title or headline of the issue")), mcp.WithString("description", mcp.Required(), mcp.Description("Detailed explanation of the issue")), mcp.WithString("issue_type", mcp.Required(), mcp.Description("Type of issue to create (common types: Bug, Task, Subtask, Story, Epic)")), ) s.AddTool(jiraCreateIssueTool, mcp.NewTypedToolHandler(jiraCreateIssueHandler)) jiraCreateChildIssueTool := mcp.NewTool("jira_create_child_issue", mcp.WithDescription("Create a child issue (sub-task) linked to a parent issue in Jira. Returns the created issue's key, ID, and URL"), mcp.WithString("parent_issue_key", mcp.Required(), mcp.Description("The parent issue key to which this child issue will be linked (e.g., KP-2)")), mcp.WithString("summary", mcp.Required(), mcp.Description("Brief title or headline of the child issue")), mcp.WithString("description", mcp.Required(), mcp.Description("Detailed explanation of the child issue")), mcp.WithString("issue_type", mcp.Description("Type of child issue to create (defaults to 'Subtask' if not specified)")), ) s.AddTool(jiraCreateChildIssueTool, mcp.NewTypedToolHandler(jiraCreateChildIssueHandler)) jiraUpdateIssueTool := mcp.NewTool("jira_update_issue", mcp.WithDescription("Modify an existing Jira issue's details. Supports partial updates - only specified fields will be changed"), mcp.WithString("issue_key", mcp.Required(), mcp.Description("The unique identifier of the issue to update (e.g., KP-2)")), mcp.WithString("summary", mcp.Description("New title for the issue (optional)")), mcp.WithString("description", mcp.Description("New description for the issue (optional)")), ) s.AddTool(jiraUpdateIssueTool, mcp.NewTypedToolHandler(jiraUpdateIssueHandler)) jiraListIssueTypesTool := mcp.NewTool("jira_list_issue_types", mcp.WithDescription("List all available issue types in a Jira project with their IDs, names, descriptions, and other attributes"), mcp.WithString("project_key", mcp.Required(), mcp.Description("Project identifier to list issue types for (e.g., KP, PROJ)")), ) s.AddTool(jiraListIssueTypesTool, mcp.NewTypedToolHandler(jiraListIssueTypesHandler)) } func jiraGetIssueHandler(ctx context.Context, request mcp.CallToolRequest, input GetIssueInput) (*mcp.CallToolResult, error) { client := services.JiraClient() // Parse fields parameter var fields []string if input.Fields != "" { fields = strings.Split(strings.ReplaceAll(input.Fields, " ", ""), ",") } // Parse expand parameter with default values expand := []string{"transitions", "changelog", "subtasks", "description"} if input.Expand != "" { expand = strings.Split(strings.ReplaceAll(input.Expand, " ", ""), ",") } issue, response, err := client.Issue.Get(ctx, input.IssueKey, fields, expand) if err != nil { if response != nil { return nil, fmt.Errorf("failed to get issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to get issue: %v", err) } // Use the new util function to format the issue formattedIssue := util.FormatJiraIssue(issue) return mcp.NewToolResultText(formattedIssue), nil } func jiraCreateIssueHandler(ctx context.Context, request mcp.CallToolRequest, input CreateIssueInput) (*mcp.CallToolResult, error) { client := services.JiraClient() var payload = models.IssueScheme{ Fields: &models.IssueFieldsScheme{ Summary: input.Summary, Project: &models.ProjectScheme{Key: input.ProjectKey}, Description: &models.CommentNodeScheme{ Content: []*models.CommentNodeScheme{ { Type: "text", Text: input.Description, }, }, }, IssueType: &models.IssueTypeScheme{Name: input.IssueType}, }, } issue, response, err := client.Issue.Create(ctx, &payload, nil) if err != nil { if response != nil { return nil, fmt.Errorf("failed to create issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to create issue: %v", err) } result := fmt.Sprintf("Issue created successfully!\nKey: %s\nID: %s\nURL: %s", issue.Key, issue.ID, issue.Self) return mcp.NewToolResultText(result), nil } func jiraCreateChildIssueHandler(ctx context.Context, request mcp.CallToolRequest, input CreateChildIssueInput) (*mcp.CallToolResult, error) { client := services.JiraClient() // Get the parent issue to retrieve its project parentIssue, response, err := client.Issue.Get(ctx, input.ParentIssueKey, nil, nil) if err != nil { if response != nil { return nil, fmt.Errorf("failed to get parent issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to get parent issue: %v", err) } // Default issue type is Sub-task if not specified issueType := "Subtask" if input.IssueType != "" { issueType = input.IssueType } var payload = models.IssueScheme{ Fields: &models.IssueFieldsScheme{ Summary: input.Summary, Project: &models.ProjectScheme{Key: parentIssue.Fields.Project.Key}, Description: &models.CommentNodeScheme{ Type: "text", Text: input.Description, }, IssueType: &models.IssueTypeScheme{Name: issueType}, Parent: &models.ParentScheme{Key: input.ParentIssueKey}, }, } issue, response, err := client.Issue.Create(ctx, &payload, nil) if err != nil { if response != nil { return nil, fmt.Errorf("failed to create child issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to create child issue: %v", err) } result := fmt.Sprintf("Child issue created successfully!\nKey: %s\nID: %s\nURL: %s\nParent: %s", issue.Key, issue.ID, issue.Self, input.ParentIssueKey) if issueType == "Bug" { result += "\n\nA bug should be linked to a Story or Task. Next step should be to create relationship between the bug and the story or task." } return mcp.NewToolResultText(result), nil } func jiraUpdateIssueHandler(ctx context.Context, request mcp.CallToolRequest, input UpdateIssueInput) (*mcp.CallToolResult, error) { client := services.JiraClient() payload := &models.IssueScheme{ Fields: &models.IssueFieldsScheme{}, } if input.Summary != "" { payload.Fields.Summary = input.Summary } if input.Description != "" { payload.Fields.Description = &models.CommentNodeScheme{ Type: "text", Text: input.Description, } } response, err := client.Issue.Update(ctx, input.IssueKey, true, payload, nil, nil) if err != nil { if response != nil { return nil, fmt.Errorf("failed to update issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to update issue: %v", err) } return mcp.NewToolResultText("Issue updated successfully!"), nil } func jiraListIssueTypesHandler(ctx context.Context, request mcp.CallToolRequest, input ListIssueTypesInput) (*mcp.CallToolResult, error) { client := services.JiraClient() issueTypes, response, err := client.Issue.Type.Gets(ctx) if err != nil { if response != nil { return nil, fmt.Errorf("failed to get issue types: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to get issue types: %v", err) } if len(issueTypes) == 0 { return mcp.NewToolResultText("No issue types found for this project."), nil } var result strings.Builder result.WriteString("Available Issue Types:\n\n") for _, issueType := range issueTypes { subtaskType := "" if issueType.Subtask { subtaskType = " (Subtask Type)" } result.WriteString(fmt.Sprintf("ID: %s\nName: %s%s\n", issueType.ID, issueType.Name, subtaskType)) if issueType.Description != "" { result.WriteString(fmt.Sprintf("Description: %s\n", issueType.Description)) } if issueType.IconURL != "" { result.WriteString(fmt.Sprintf("Icon URL: %s\n", issueType.IconURL)) } if issueType.Scope != nil { result.WriteString(fmt.Sprintf("Scope: %s\n", issueType.Scope.Type)) } result.WriteString("\n") } return mcp.NewToolResultText(result.String()), nil } ``` -------------------------------------------------------------------------------- /specs/001-i-want-to/quickstart.md: -------------------------------------------------------------------------------- ```markdown # Quickstart: Get Development Information from Jira Issue **Feature**: Retrieve branches, pull requests, and commits linked to a Jira issue **Tool**: `jira_get_development_information` **Status**: Implementation pending (planned for Phase 2) ## Overview The `jira_get_development_information` tool retrieves all development work linked to a Jira issue through VCS integrations (GitHub, GitLab, Bitbucket). This includes: - **Branches**: Git branches that reference the issue key in their name - **Pull Requests**: PRs/MRs that reference the issue key - **Commits**: Commits that mention the issue key in their message ## Prerequisites 1. **Jira Instance**: You have access to a Jira Cloud or Jira Data Center instance 2. **Authentication**: You have configured `ATLASSIAN_HOST`, `ATLASSIAN_EMAIL`, and `ATLASSIAN_TOKEN` environment variables 3. **VCS Integration**: Your Jira instance has development tool integrations enabled (GitHub for Jira, GitLab for Jira, or Bitbucket integration) 4. **Permissions**: You have permission to view the issue and its development information ## Basic Usage ### Get All Development Information Retrieve all branches, pull requests, and commits for an issue: ```bash # Using Claude or another MCP client jira_get_development_information { "issue_key": "PROJ-123" } ``` **Example Output**: ``` Development Information for PROJ-123: === Branches (2) === Branch: feature/PROJ-123-login Repository: company/backend-api Last Commit: abc1234 - "Add login endpoint" URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login Branch: feature/PROJ-123-ui Repository: company/frontend Last Commit: def5678 - "Add login form" URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui === Pull Requests (1) === PR #42: Add login functionality Status: OPEN Author: John Doe ([email protected]) Repository: company/backend-api URL: https://github.com/company/backend-api/pull/42 Last Updated: 2025-10-07 14:30:00 === Commits (3) === Repository: company/backend-api Commit: abc1234 (Oct 7, 14:00) Author: John Doe Message: Add login endpoint [PROJ-123] URL: https://github.com/company/backend-api/commit/abc1234 Commit: xyz9876 (Oct 7, 13:00) Author: Jane Smith Message: Update authentication model URL: https://github.com/company/backend-api/commit/xyz9876 ``` ### Get Only Branches Filter to show only branches: ```bash jira_get_development_information { "issue_key": "PROJ-123", "include_branches": true, "include_pull_requests": false, "include_commits": false } ``` **Example Output**: ``` Development Information for PROJ-123: === Branches (2) === Branch: feature/PROJ-123-login Repository: company/backend-api Last Commit: abc1234 - "Add login endpoint" URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login Branch: feature/PROJ-123-ui Repository: company/frontend Last Commit: def5678 - "Add login form" URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui ``` ### Get Only Pull Requests Filter to show only pull requests: ```bash jira_get_development_information { "issue_key": "PROJ-123", "include_branches": false, "include_pull_requests": true, "include_commits": false } ``` ### Get Branches and Pull Requests (No Commits) Exclude commits for a cleaner view: ```bash jira_get_development_information { "issue_key": "PROJ-123", "include_commits": false } ``` ## Common Scenarios ### Scenario 1: Check Development Status **Use case**: You want to see if any code has been written for a user story. ```bash jira_get_development_information { "issue_key": "TEAM-456" } ``` **What to look for**: - Number of branches (indicates work in progress) - PR status (OPEN = in review, MERGED = completed) - Commit count (indicates activity level) --- ### Scenario 2: Review Pull Request Status **Use case**: You want to check if PRs are ready for merge. ```bash jira_get_development_information { "issue_key": "TEAM-456", "include_pull_requests": true, "include_branches": false, "include_commits": false } ``` **What to look for**: - Status: OPEN (needs review), MERGED (done), DECLINED (rejected) - Author: Who submitted the PR - Last Updated: How recent the PR is --- ### Scenario 3: Find Linked Branches **Use case**: You need to know which branches are working on this issue. ```bash jira_get_development_information { "issue_key": "TEAM-456", "include_branches": true, "include_pull_requests": false, "include_commits": false } ``` **What to look for**: - Branch names (indicates work streams) - Repository (which codebase is affected) - Last commit (recent activity) --- ### Scenario 4: Review Commit History **Use case**: You want to understand what code changes were made. ```bash jira_get_development_information { "issue_key": "TEAM-456", "include_commits": true, "include_branches": false, "include_pull_requests": false } ``` **What to look for**: - Commit messages (what was changed) - Authors (who worked on it) - Timestamps (when work happened) ## Error Handling ### Issue Not Found **Error**: ``` failed to retrieve development information: issue not found (endpoint: /rest/api/3/issue/PROJ-999) ``` **Solution**: Verify the issue key is correct and you have permission to view it. --- ### No Development Information **Output**: ``` Development Information for PROJ-123: No branches, pull requests, or commits found. This may mean: - No development work has been linked to this issue - The Jira-GitHub/GitLab/Bitbucket integration is not configured - You lack permissions to view development information ``` **Solutions**: - Check if VCS integration is enabled in Jira - Verify branches/PRs/commits reference the issue key (e.g., "PROJ-123" in branch name or commit message) - Ask your Jira admin to check integration configuration --- ### Invalid Issue Key Format **Error**: ``` invalid issue key format: invalid-key (expected format: PROJ-123) ``` **Solution**: Use the correct format: uppercase project key + dash + number (e.g., PROJ-123, TEAM-456) --- ### Authentication Error **Error**: ``` failed to retrieve development information: authentication failed (endpoint: /rest/dev-status/1.0/issue/detail) ``` **Solution**: Check your `ATLASSIAN_TOKEN` is valid and has appropriate permissions. ## Tips and Best Practices ### 1. Use Filters for Large Issues For issues with many commits (50+), use filters to reduce noise: ```bash # Focus on high-level view (branches and PRs only) jira_get_development_information { "issue_key": "PROJ-123", "include_commits": false } ``` ### 2. Cross-Reference with Issue Status Development information shows code activity, but doesn't reflect Jira issue status: - **Branches exist + Issue "To Do"** → Work started but not tracked in Jira - **PR merged + Issue "In Progress"** → Update Jira status to "Done" - **No branches + Issue "In Progress"** → Development hasn't started yet ### 3. Check Multiple Issues at Once Use the tool repeatedly to check status across multiple issues: ```bash # Check epic and its subtasks jira_get_development_information {"issue_key": "PROJ-100"} # Epic jira_get_development_information {"issue_key": "PROJ-101"} # Subtask 1 jira_get_development_information {"issue_key": "PROJ-102"} # Subtask 2 ``` ### 4. Verify VCS Integration If results are empty, verify integration: 1. Go to Jira → Project Settings → Development Tools 2. Check GitHub/GitLab/Bitbucket integration is enabled 3. Verify repository is linked to the project 4. Test by creating a branch with issue key in the name ### 5. Branch Naming Convention To ensure branches are detected: - **Good**: `feature/PROJ-123-login`, `bugfix/PROJ-123`, `PROJ-123-refactor` - **Bad**: `my-feature-branch` (no issue key) Commits should reference issue key in message: - **Good**: `"Add login [PROJ-123]"`, `"Fix bug (PROJ-123)"` - **Bad**: `"Fixed stuff"` (no issue key) ## Advanced Usage ### Combine with Other Tools Check development status alongside issue details: ```bash # Get issue details jira_get_issue {"issue_key": "PROJ-123"} # Get development information jira_get_development_information {"issue_key": "PROJ-123"} # Check transitions available jira_get_issue { "issue_key": "PROJ-123", "expand": "transitions" } ``` ### Track Progress Across Team Check development activity for all issues in a sprint: ```bash # Search issues in sprint jira_search_issue { "jql": "Sprint = 15 AND status != Done" } # For each issue, check development information jira_get_development_information {"issue_key": "PROJ-123"} jira_get_development_information {"issue_key": "PROJ-124"} jira_get_development_information {"issue_key": "PROJ-125"} ``` ## Limitations 1. **Undocumented API**: Uses Jira's internal dev-status API which may change without notice 2. **Sync Delay**: Development information is synced periodically (typically every few minutes), not real-time 3. **Commit Limits**: Only recent commits are included (API may limit to 50-100) 4. **VCS-Specific**: Only works with Jira-integrated VCS (GitHub for Jira, GitLab for Jira, Bitbucket) 5. **Permissions**: Respects Jira permissions, not VCS permissions (you may see references to private repos you can't access) ## Troubleshooting ### Problem: Empty results but branches exist **Possible causes**: - Branch/commit doesn't reference issue key - VCS integration sync is delayed (wait 5-10 minutes) - Repository not linked to Jira project **Solution**: Check branch names and commit messages include issue key (e.g., "PROJ-123") --- ### Problem: Seeing branches from other issues **Explanation**: If a branch or commit references multiple issue keys (e.g., "PROJ-123 and PROJ-124"), it will appear in results for both issues. This is expected behavior. --- ### Problem: PR status shows OPEN but it's merged in GitHub **Explanation**: Sync delay. Jira updates development information every 5-15 minutes. Wait and try again. --- ### Problem: Missing commits **Explanation**: The API limits the number of commits returned (typically 50-100 most recent). Older commits may not appear. ## Next Steps - **Create Issues**: Use `jira_create_issue` to create tasks - **Search Issues**: Use `jira_search_issue` with JQL to find issues - **Transition Issues**: Use `jira_transition_issue` to update status after PRs are merged - **Add Comments**: Use `jira_add_comment` to document development findings ## Support For issues with this tool: - Check Jira VCS integration configuration - Verify branch/commit naming includes issue keys - Contact your Jira administrator for integration support For bugs in this MCP tool: - Report at: https://github.com/nguyenvanduocit/jira-mcp/issues ``` -------------------------------------------------------------------------------- /specs/001-i-want-to/research.md: -------------------------------------------------------------------------------- ```markdown # Research Report: Development Information Retrieval **Feature**: Retrieve Development Information from Jira Issue **Date**: 2025-10-07 **Status**: Completed ## Research Task 1: go-atlassian Development Information API ### Decision The go-atlassian library (v1.6.1) **does not provide** a built-in method for the `/rest/dev-status/1.0/issue/detail` endpoint. We will use the library's generic `NewRequest()` and `Call()` methods to access this undocumented API endpoint. ### Rationale 1. **No Official Support**: The dev-status endpoint is an undocumented, internal Atlassian API not included in go-atlassian's typed service methods 2. **Generic Methods Available**: The library provides `client.NewRequest()` and `client.Call()` for custom API calls 3. **Community Validation**: The endpoint is widely used in the community with established response structures 4. **Risk Acceptable**: While unofficial, the endpoint provides critical functionality not available through official APIs ### Implementation Approach #### Method Signature ```go // From go-atlassian v1.6.1 client func (c *Client) NewRequest(ctx context.Context, method, urlStr, type_ string, body interface{}) (*http.Request, error) func (c *Client) Call(request *http.Request, structure interface{}) (*models.ResponseScheme, error) ``` #### Response Structure (Custom Types Required) ```go type DevStatusResponse struct { Errors []string `json:"errors"` Detail []DevStatusDetail `json:"detail"` } type DevStatusDetail struct { Branches []Branch `json:"branches"` PullRequests []PullRequest `json:"pullRequests"` Repositories []Repository `json:"repositories"` } type Branch struct { Name string `json:"name"` URL string `json:"url"` Repository Repository `json:"repository"` LastCommit Commit `json:"lastCommit"` } type PullRequest struct { ID string `json:"id"` Name string `json:"name"` URL string `json:"url"` Status string `json:"status"` // OPEN, MERGED, DECLINED Author Author `json:"author"` LastUpdate string `json:"lastUpdate"` } type Repository struct { Name string `json:"name"` URL string `json:"url"` Commits []Commit `json:"commits"` } type Commit struct { ID string `json:"id"` DisplayID string `json:"displayId"` Message string `json:"message"` Author Author `json:"author"` AuthorTimestamp string `json:"authorTimestamp"` URL string `json:"url"` } type Author struct { Name string `json:"name"` Email string `json:"email,omitempty"` Avatar string `json:"avatar,omitempty"` } ``` #### Error Handling 1. **404 Not Found**: Issue doesn't exist or has no development information 2. **401 Unauthorized**: Authentication failure 3. **400 Bad Request**: Invalid parameters (must use numeric issue ID, not issue key) 4. **500 Internal Server Error**: Jira server error 5. **Empty Detail Array**: No development information linked 6. **Errors Array**: Check `response.Errors` for API-specific error messages #### Critical Requirements - **Numeric Issue ID Required**: Must convert issue key to numeric ID first via standard issue endpoint - **Query Parameters**: `issueId={id}&applicationType={github|bitbucket|stash}&dataType={repository|pullrequest|branch}` - **API Instability Warning**: Undocumented endpoint can change without notice ### Alternatives Considered 1. **Official Jira API**: No official API provides branch/PR information - rejected 2. **Direct Git Provider APIs**: Would require separate GitHub/GitLab/Bitbucket credentials - rejected for complexity 3. **Webhooks/Events**: Real-time but doesn't support querying historical data - rejected ### References - go-atlassian GitHub: https://github.com/ctreminiom/go-atlassian - Atlassian Community discussions on dev-status endpoint - Source code: `/Volumes/Data/Projects/claudeserver/jira-mcp/go/pkg/mod/github.com/ctreminiom/[email protected]` --- ## Research Task 2: Output Formatting Best Practices ### Decision Use **inline formatting with string builder** for development information output. Do NOT create a `util.Format*` function. ### Rationale 1. **Project Convention**: CLAUDE.md explicitly states "avoid util, helper functions, keep things simple" 2. **No Code Duplication**: Development info formatting will be used in a single tool, not 3+ tools 3. **Consistency**: Comments, worklogs, versions, and relationships tools all use inline formatting 4. **Simpler Data Structure**: Development entities are simpler than Jira issues (which justified `util.FormatJiraIssue`) ### Format Structure #### Pattern for Development Information Display ``` Development Information for PROJ-123: === Branches (2) === Branch: feature/PROJ-123-login Repository: company/backend-api Last Commit: abc1234 - "Add login endpoint" URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login Branch: feature/PROJ-123-ui Repository: company/frontend Last Commit: def5678 - "Add login form" URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui === Pull Requests (1) === PR #42: Add login functionality Status: OPEN Author: John Doe ([email protected]) Repository: company/backend-api URL: https://github.com/company/backend-api/pull/42 Last Updated: 2025-10-07 14:30:00 === Commits (3) === Repository: company/backend-api Commit: abc1234 (Oct 7, 14:00) Author: John Doe Message: Add login endpoint URL: https://github.com/company/backend-api/commit/abc1234 Commit: xyz9876 (Oct 7, 13:00) Author: Jane Smith Message: Update authentication model URL: https://github.com/company/backend-api/commit/xyz9876 ``` #### Empty State Handling ``` Development Information for PROJ-123: No branches, pull requests, or commits found. This may mean: - No development work has been linked to this issue - The Jira-GitHub/GitLab/Bitbucket integration is not configured - You lack permissions to view development information ``` #### Error Message Format ``` Failed to retrieve development information: Issue not found (endpoint: /rest/dev-status/1.0/issue/detail?issueId=12345) ``` ### Key Formatting Principles 1. **Plain Text Only**: No markdown formatting (`#`, `**`, etc.) 2. **Section Headers**: Use `===` separators and counts (e.g., "Branches (2)") 3. **Hierarchical Indentation**: Use 2-space indents for nested items 4. **Concise Labels**: Use short, clear field names (e.g., "Status:" not "Pull Request Status:") 5. **Contextual URLs**: Always include full URLs for easy navigation 6. **Conditional Rendering**: Gracefully handle missing fields (e.g., "Author: Unknown") 7. **Grouping by Repository**: Group commits and branches by repository for clarity ### Alternatives Considered 1. **JSON Output**: More machine-readable but less LLM-friendly - rejected 2. **Markdown Format**: Not used elsewhere in codebase - rejected for consistency 3. **util.FormatDevelopmentInfo Function**: Premature extraction before duplication - rejected per conventions ### References - Existing formatters: `/Volumes/Data/Projects/claudeserver/jira-mcp/util/jira_formatter.go` - Tool patterns: `tools/jira_worklog.go`, `tools/jira_comment.go`, `tools/jira_version.go` --- ## Research Task 3: Filter Parameter Design ### Decision Use **optional boolean flags** for filtering: `include_branches`, `include_pull_requests`, `include_commits` with **default true** (all types included). ### Rationale 1. **LLM Usability**: Boolean flags are simpler for LLMs to reason about than enum values 2. **Explicit Intent**: Separate flags make filtering intentions clear 3. **Flexible Combinations**: Users can request any combination (e.g., just branches and PRs, not commits) 4. **Default Behavior**: Include all by default to match user expectation of "get all development information" 5. **Consistency**: Mirrors patterns in `jira_get_issue` tool which has multiple optional expand flags ### Parameter Structure ```go type GetDevelopmentInfoInput struct { IssueKey string `json:"issue_key" validate:"required"` IncludeBranches bool `json:"include_branches,omitempty"` // Default: true IncludePullRequests bool `json:"include_pull_requests,omitempty"` // Default: true IncludeCommits bool `json:"include_commits,omitempty"` // Default: true } ``` ### Tool Registration ```go tool := mcp.NewTool("jira_get_development_information", mcp.WithDescription("Retrieve branches, pull requests, and commits linked to a Jira issue via development tool integrations (GitHub, GitLab, Bitbucket)"), mcp.WithString("issue_key", mcp.Required(), mcp.Description("The Jira issue key (e.g., PROJ-123)")), mcp.WithBoolean("include_branches", mcp.Description("Include branches in the response (default: true)")), mcp.WithBoolean("include_pull_requests", mcp.Description("Include pull requests in the response (default: true)")), mcp.WithBoolean("include_commits", mcp.Description("Include commits in the response (default: true)")), ) ``` ### Handler Logic ```go func jiraGetDevelopmentInfoHandler(ctx context.Context, request mcp.CallToolRequest, input GetDevelopmentInfoInput) (*mcp.CallToolResult, error) { // Default all filters to true if not explicitly set to false includeBranches := input.IncludeBranches || isOmitted(input.IncludeBranches) includePRs := input.IncludePullRequests || isOmitted(input.IncludePullRequests) includeCommits := input.IncludeCommits || isOmitted(input.IncludeCommits) // Fetch data devInfo, err := fetchDevInfo(ctx, input.IssueKey) if err != nil { return nil, err } // Filter output based on flags var sb strings.Builder if includeBranches && len(devInfo.Branches) > 0 { sb.WriteString(formatBranches(devInfo.Branches)) } if includePRs && len(devInfo.PullRequests) > 0 { sb.WriteString(formatPullRequests(devInfo.PullRequests)) } if includeCommits && len(devInfo.Commits) > 0 { sb.WriteString(formatCommits(devInfo.Commits)) } return mcp.NewToolResultText(sb.String()), nil } ``` ### Usage Examples ```javascript // Get all development information (default) { "issue_key": "PROJ-123" } // Get only branches { "issue_key": "PROJ-123", "include_branches": true, "include_pull_requests": false, "include_commits": false } // Get branches and pull requests, skip commits { "issue_key": "PROJ-123", "include_commits": false } ``` ### Alternatives Considered 1. **Enum String Parameter**: `filter_type: "branches|pull_requests|commits"` - Rejected: Can't combine multiple types easily 2. **String Array Parameter**: `types: ["branches", "commits"]` - Rejected: More complex for LLMs to construct, requires array handling 3. **Single Include/Exclude List**: `include: ["branches"], exclude: ["commits"]` - Rejected: Redundant and confusing - only need one direction 4. **Default False (Opt-in)**: Require explicit true for each type - Rejected: Burdensome default - users expect "get all" behavior 5. **No Filtering**: Always return all types - Rejected: Reduces flexibility and increases noise when users only need specific data ### References - Similar patterns: `jira_get_issue` tool with `fields` and `expand` parameters - go-atlassian API: No built-in filtering support, filtering done client-side --- ## Summary All three research tasks are complete with clear decisions: 1. **API Integration**: Use go-atlassian's generic `NewRequest()`/`Call()` methods with custom types for the undocumented dev-status endpoint 2. **Output Formatting**: Inline string builder formatting with plain text, grouped by type (branches/PRs/commits), no util function needed 3. **Filtering**: Optional boolean flags (`include_branches`, `include_pull_requests`, `include_commits`) defaulting to true These decisions enable implementation to proceed to Phase 1 (data model and contracts). ``` -------------------------------------------------------------------------------- /.specify/memory/constitution.md: -------------------------------------------------------------------------------- ```markdown <!-- Sync Impact Report ================== Version Change: 1.0.0 → 1.1.0 (Minor - Expanded guidance on typed tools, ADF formatting, and development information) Modified Principles: - Type Safety & Validation: Enhanced with comprehensive typed tools guidance from migration - AI-First Output Design: Added Atlassian Document Format (ADF) requirements for comments Added Sections: - VII. Development Information Integration principle - Enhanced Tool Implementation Standards with ADF comment formatting - Expanded Type Safety section with typed tools migration patterns Removed Sections: None Templates Updated: ✅ plan-template.md - Constitution Check already comprehensive ✅ spec-template.md - Functional Requirements examples already reflect MCP patterns ✅ tasks-template.md - Implementation phase already reflects typed handlers Follow-up TODOs: None --> # Jira MCP Constitution ## Core Principles ### I. MCP Protocol Compliance (NON-NEGOTIABLE) Every feature MUST be exposed as an MCP tool. Direct API access or non-MCP interfaces are forbidden. **Requirements:** - All functionality accessible via `mcp.NewTool` registration - Tools registered in `main.go` via `RegisterJira<Category>Tool` functions - STDIO mode as default, HTTP mode optional for development only - Tool names MUST follow `jira_<operation>` naming convention for LLM discoverability **Rationale:** MCP is the contract with AI assistants. Breaking this breaks the entire integration. ### II. AI-First Output Design All tool responses MUST be formatted for AI/LLM consumption, prioritizing readability over machine parsing. **Requirements:** - Use `util.Format*` functions for consistent human-readable output - Return text format via `mcp.NewToolResultText` as primary response type - Include context in output (e.g., "Issue created successfully!" with key/URL) - Structured data uses clear labels and hierarchical formatting - Error messages include actionable context (endpoint, status, hint) - Comments MUST use Atlassian Document Format (ADF) with proper structure (see Tool Implementation Standards) **Rationale:** The end consumer is an LLM, not a human or parsing script. Output must be self-documenting. Jira API requires ADF for rich text fields like comments. ### III. Simplicity Over Abstraction Avoid unnecessary utility functions, helper layers, and organizational-only abstractions. **Requirements:** - No "managers", "facades", or "orchestrators" unless essential complexity justifies them - Direct client calls preferred over wrapper functions - Formatting utilities allowed only when used across 3+ tools - Keep handler logic inline - don't extract single-use helper methods - Complexity violations MUST be documented in implementation plan **Rationale:** Go's simplicity is a feature. Extra layers harm readability and maintenance. Per project guidance: "avoid util, helper functions, keep things simple." ### IV. Type Safety & Validation All tool inputs MUST use structured types with JSON tags and validation annotations. **Requirements:** - Define `<Operation>Input` structs for each tool handler - Use JSON tags matching MCP parameter names (`json:"field_name"`) - Add `validate:"required"` for mandatory fields - Use `json:"field,omitempty"` for optional fields - Use typed handlers: `mcp.NewTypedToolHandler(handler)` - Handler signatures: `func(ctx context.Context, request mcp.CallToolRequest, input <Type>) (*mcp.CallToolResult, error)` **Migration Pattern (from typed-tools-migration.md):** ```go // 1. Define input struct with validation type GetIssueInput struct { IssueKey string `json:"issue_key" validate:"required"` Fields string `json:"fields,omitempty"` Expand string `json:"expand,omitempty"` } // 2. Update handler signature to accept typed input func jiraGetIssueHandler(ctx context.Context, request mcp.CallToolRequest, input GetIssueInput) (*mcp.CallToolResult, error) { client := services.JiraClient() // Direct access to validated parameters - no type assertions needed issue, response, err := client.Issue.Get(ctx, input.IssueKey, fields, expand) // ... } // 3. Register with typed handler wrapper s.AddTool(jiraGetIssueTool, mcp.NewTypedToolHandler(jiraGetIssueHandler)) ``` **Benefits:** - Compile-time type safety prevents runtime errors - Automatic validation via `validate` tags - Eliminates manual parameter extraction and type assertions - Reduces boilerplate code by 30-40% - IDE autocomplete and type checking support **Rationale:** Type safety catches errors at compile time. Validation ensures LLMs provide correct parameters. Typed tools improve developer experience and code maintainability. ### V. Resource Efficiency Client connections and expensive resources MUST use singleton patterns. **Requirements:** - `services.JiraClient()` implemented with `sync.OnceValue` - `services.AgileClient()` implemented with `sync.OnceValue` - Single Jira client instance reused across all tool invocations - No connection pooling or per-request client creation - HTTP server (when used) shares same singleton client **Rationale:** MCP servers are long-running processes. Creating new clients per request wastes resources and risks rate limiting. ### VI. Error Transparency Errors MUST provide sufficient context for debugging without access to logs. **Requirements:** - Include endpoint URL in API error messages - Include response body when available: `response.Bytes.String()` - Use clear prefixes: "failed to <operation>: <details>" - Return structured error text via `return nil, fmt.Errorf(...)` - Validation errors mention field name and expected format **Error Pattern:** ```go result, response, err := client.Operation(ctx, params...) if err != nil { if response != nil { return nil, fmt.Errorf("failed to <op>: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to <op>: %v", err) } ``` **Rationale:** Users debug through AI assistants reading error messages. Opaque errors create friction. ### VII. Development Information Integration Tools that expose issue data MUST include development information (branches, PRs, commits) when available. **Requirements:** - Use `client.Issue.Metadata.Get()` to fetch development information - Check for development details in metadata: `DevelopmentInformation.Details` - Format development info with clear sections for branches, pull requests, commits - Include repository names, branch names, PR titles/status, commit messages - Handle missing development information gracefully **Example:** ```go // Get development information metadata, metaResponse, metaErr := client.Issue.Metadata.Get(ctx, issueKey) if metaErr == nil && metadata != nil && metadata.Fields != nil { if devInfo := metadata.Fields.DevelopmentInformation; devInfo != nil && len(devInfo.Details) > 0 { formattedOutput += util.FormatDevelopmentInfo(devInfo.Details) } } ``` **Rationale:** AI assistants benefit from seeing the complete context of an issue, including related code changes. This enables better recommendations and understanding of implementation status. ## Tool Implementation Standards ### Registration Pattern **MUST follow this exact structure:** ```go func RegisterJira<Category>Tool(s *server.MCPServer) { tool := mcp.NewTool("jira_<operation>", mcp.WithDescription("..."), mcp.WithString/Number/Boolean("<param>", mcp.Required(), mcp.Description("...")), ) s.AddTool(tool, mcp.NewTypedToolHandler(<handler>)) } ``` ### Handler Pattern **MUST follow this exact signature:** ```go func jira<Operation>Handler(ctx context.Context, request mcp.CallToolRequest, input <Type>Input) (*mcp.CallToolResult, error) { client := services.JiraClient() // Extract/validate parameters (if complex) // Make API call result, response, err := client.<API>.<Method>(ctx, ...) if err != nil { if response != nil { return nil, fmt.Errorf("failed to <op>: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint) } return nil, fmt.Errorf("failed to <op>: %v", err) } // Format response formatted := util.Format<Entity>(result) return mcp.NewToolResultText(formatted), nil } ``` ### ADF Comment Formatting Comments MUST use Atlassian Document Format (ADF) structure: ```go func buildADFComment(text string) *models.CommentPayloadSchemeV2 { return &models.CommentPayloadSchemeV2{ Body: &models.CommentNodeScheme{ Version: 1, Type: "doc", Content: []*models.CommentNodeScheme{ { Type: "paragraph", Content: []*models.CommentNodeScheme{ { Type: "text", Text: text, }, }, }, }, }, } } ``` **Requirements:** - Version MUST be 1 - Root type MUST be "doc" - Content MUST be wrapped in paragraph nodes - Text nodes contain actual comment text ### Tool Naming Convention - Prefix: `jira_` (REQUIRED for LLM discoverability) - Operation: Action verb in present tense (get, create, update, list, add, move) - Entity: Singular form (issue, sprint, comment, worklog) - Examples: `jira_get_issue`, `jira_create_issue`, `jira_list_sprints`, `jira_add_comment` ## Testing & Quality Gates ### Required Tests **Integration tests** are REQUIRED for: - New tool categories (Issue, Sprint, Comment, etc.) - Breaking changes to tool contracts (parameters, output format) - Multi-step workflows (e.g., create issue → add comment → transition) **Contract tests** ensure: - Tool registration succeeds - Required parameters are enforced - Handler returns expected result type ### Test Execution Tests MUST pass via `go test ./...` before: - Creating pull requests - Merging to main branch - Tagging releases ### Quality Checklist Before registering a new tool, verify: - [ ] Tool name follows `jira_<operation>` convention - [ ] Description is clear for LLM understanding - [ ] Input struct has validation tags - [ ] Handler uses typed pattern - [ ] Error messages include endpoint context - [ ] Output is formatted via util function (if reusable) - [ ] Tool registered in main.go - [ ] Development information included (for issue-related tools) - [ ] Comments use ADF format (if applicable) ## Governance ### Amendment Procedure 1. Propose amendment with rationale and impact analysis 2. Document which principles/sections are affected 3. Update `.specify/memory/constitution.md` with versioned changes 4. Propagate changes to affected templates (plan, spec, tasks) 5. Update CLAUDE.md if guidance changes 6. Commit with message: `docs: amend constitution to vX.Y.Z (<summary>)` ### Versioning Policy **MAJOR** (X.0.0): Principle removal, redefinition, or backward-incompatible governance changes **MINOR** (1.X.0): New principle added, materially expanded guidance, new mandatory section **PATCH** (1.0.X): Clarifications, wording fixes, example additions, non-semantic refinements ### Compliance Review **All code reviews MUST verify:** - Tools follow registration and handler patterns - Input types use validation - Errors include diagnostic context - Output formatted for AI consumption - No unnecessary abstraction layers introduced **Complexity exceptions** require: - Documentation in implementation plan's "Complexity Tracking" section - Justification: "Why needed?" and "Simpler alternative rejected because?" - Approval before implementation ### Runtime Development Guidance Developers (AI and human) working in this repository MUST consult `CLAUDE.md` for: - Development commands (build, dev, install) - Architecture overview (core structure, dependencies) - Tool implementation pattern examples - Service architecture (client initialization, STDIO/HTTP modes) - Code conventions `CLAUDE.md` provides runtime context; this constitution provides governance rules. Both are authoritative. **Version**: 1.1.0 | **Ratified**: 2025-10-07 | **Last Amended**: 2025-10-07 ``` -------------------------------------------------------------------------------- /specs/001-i-want-to/data-model.md: -------------------------------------------------------------------------------- ```markdown # Data Model: Development Information **Feature**: Retrieve Development Information from Jira Issue **Date**: 2025-10-07 **Based on**: Research findings from research.md ## Overview This document defines the Go types for representing development information retrieved from Jira's dev-status API. These types map the undocumented `/rest/dev-status/1.0/issue/detail` endpoint response structure. --- ## Core Entities ### DevStatusResponse Top-level response container from the dev-status API. ```go type DevStatusResponse struct { Errors []string `json:"errors"` Detail []DevStatusDetail `json:"detail"` } ``` **Fields**: - `Errors`: Array of error messages (empty on success) - `Detail`: Array of development information, typically one element per VCS integration (GitHub, GitLab, Bitbucket) **Validation Rules**: - Check `len(Errors) > 0` before processing `Detail` - `Detail` may be empty if no development information exists **Relationships**: - Contains: `DevStatusDetail` (1-to-many, one per VCS integration) --- ### DevStatusDetail Container for all development entities from a single VCS integration. ```go type DevStatusDetail struct { Branches []Branch `json:"branches"` PullRequests []PullRequest `json:"pullRequests"` Repositories []Repository `json:"repositories"` } ``` **Fields**: - `Branches`: All branches referencing the issue key - `PullRequests`: All pull/merge requests referencing the issue key - `Repositories`: Repositories containing commits that reference the issue key **Validation Rules**: - All arrays may be empty - No guaranteed ordering **Relationships**: - Contains: `Branch` (0-to-many) - Contains: `PullRequest` (0-to-many) - Contains: `Repository` (0-to-many) --- ### Branch Represents a Git branch linked to the Jira issue. ```go type Branch struct { Name string `json:"name"` URL string `json:"url"` CreatePullRequestURL string `json:"createPullRequestUrl,omitempty"` Repository Repository `json:"repository"` LastCommit Commit `json:"lastCommit"` } ``` **Fields**: - `Name`: Branch name (e.g., "feature/PROJ-123-login") - `URL`: Direct link to branch in VCS (GitHub, GitLab, Bitbucket) - `CreatePullRequestURL`: Link to create PR from this branch (optional) - `Repository`: Repository containing the branch - `LastCommit`: Most recent commit on this branch **Validation Rules**: - `Name` is always present - `URL` may be empty if VCS integration doesn't provide it - `CreatePullRequestURL` is optional **Relationships**: - Belongs to: `Repository` - Has one: `LastCommit` --- ### PullRequest Represents a pull request or merge request linked to the Jira issue. ```go type PullRequest struct { ID string `json:"id"` Name string `json:"name"` URL string `json:"url"` Status string `json:"status"` Author Author `json:"author"` LastUpdate string `json:"lastUpdate"` Source BranchRef `json:"source"` Destination BranchRef `json:"destination"` } ``` **Fields**: - `ID`: Unique identifier from VCS (e.g., "42" for PR #42) - `Name`: PR title - `URL`: Direct link to PR in VCS - `Status`: Current state - valid values: `OPEN`, `MERGED`, `DECLINED`, `CLOSED` - `Author`: Person who created the PR - `LastUpdate`: ISO 8601 timestamp of last update - `Source`: Branch being merged from - `Destination`: Branch being merged into **Validation Rules**: - `Status` should be one of: OPEN, MERGED, DECLINED, CLOSED - `LastUpdate` format: `"2025-10-07T14:30:00.000+0000"` **State Transitions**: - OPEN → MERGED (PR approved and merged) - OPEN → DECLINED (PR rejected/closed without merging) - OPEN → CLOSED (PR closed without merging) **Relationships**: - Has one: `Author` - References: `BranchRef` (source and destination) --- ### Repository Represents a Git repository containing development work for the issue. ```go type Repository struct { Name string `json:"name"` URL string `json:"url"` Avatar string `json:"avatar,omitempty"` Commits []Commit `json:"commits,omitempty"` } ``` **Fields**: - `Name`: Repository name (e.g., "company/backend-api") - `URL`: Direct link to repository in VCS - `Avatar`: Repository avatar image URL (optional) - `Commits`: Array of commits referencing the issue (optional, only present in `Repositories` array) **Validation Rules**: - `Name` is always present - `Commits` array only populated in the `Repositories` collection, empty in `Branch.Repository` **Relationships**: - Contains: `Commit` (0-to-many, only in repositories list) - Referenced by: `Branch` - Referenced by: `BranchRef` --- ### Commit Represents a Git commit linked to the Jira issue. ```go type Commit struct { ID string `json:"id"` DisplayID string `json:"displayId"` Message string `json:"message"` Author Author `json:"author"` AuthorTimestamp string `json:"authorTimestamp"` URL string `json:"url,omitempty"` FileCount int `json:"fileCount,omitempty"` Merge bool `json:"merge,omitempty"` } ``` **Fields**: - `ID`: Full commit SHA (e.g., "abc123def456...") - `DisplayID`: Abbreviated commit SHA (e.g., "abc123d") - `Message`: Commit message (first line typically) - `Author`: Person who authored the commit - `AuthorTimestamp`: ISO 8601 timestamp of commit - `URL`: Direct link to commit in VCS (optional) - `FileCount`: Number of files changed (optional, may be 0) - `Merge`: Whether this is a merge commit (optional) **Validation Rules**: - `ID` and `DisplayID` are always present - `Message` may be empty (rare but possible) - `AuthorTimestamp` format: `"2025-10-07T14:30:00.000+0000"` **Relationships**: - Has one: `Author` - Belongs to: `Repository` (implicitly) - Referenced by: `Branch.LastCommit` --- ### Author Represents the author of a commit or pull request. ```go type Author struct { Name string `json:"name"` Email string `json:"email,omitempty"` Avatar string `json:"avatar,omitempty"` } ``` **Fields**: - `Name`: Display name (e.g., "John Doe") - `Email`: Email address (optional, may be redacted by VCS) - `Avatar`: Profile picture URL (optional) **Validation Rules**: - `Name` is always present - `Email` may be empty or redacted (e.g., "[email protected]") - `Avatar` may be empty **Relationships**: - Referenced by: `Commit` - Referenced by: `PullRequest` --- ### BranchRef Represents a branch reference (used in pull requests). ```go type BranchRef struct { Branch string `json:"branch"` Repository string `json:"repository"` } ``` **Fields**: - `Branch`: Branch name (e.g., "feature/PROJ-123") - `Repository`: Repository identifier (e.g., "company/backend-api") **Validation Rules**: - Both fields are always present - `Repository` format varies by VCS (GitHub: "org/repo", GitLab: "group/project") **Relationships**: - References: Repository (by name) - Used by: `PullRequest.Source` and `PullRequest.Destination` --- ## Tool Input Structure ### GetDevelopmentInfoInput Input parameters for the `jira_get_development_information` tool. ```go type GetDevelopmentInfoInput struct { IssueKey string `json:"issue_key" validate:"required"` IncludeBranches bool `json:"include_branches,omitempty"` IncludePullRequests bool `json:"include_pull_requests,omitempty"` IncludeCommits bool `json:"include_commits,omitempty"` } ``` **Fields**: - `IssueKey`: Jira issue key (e.g., "PROJ-123") - REQUIRED - `IncludeBranches`: Include branches in response (default: true) - `IncludePullRequests`: Include pull requests in response (default: true) - `IncludeCommits`: Include commits in response (default: true) **Validation Rules**: - `IssueKey` must match pattern: `[A-Z]+-\d+` (e.g., PROJ-123) - All boolean flags are optional and default to true - At least one include flag should be true (though not enforced) --- ## Entity Relationships Diagram ``` DevStatusResponse └── Detail [] └── DevStatusDetail ├── Branches [] │ └── Branch │ ├── Repository │ └── LastCommit (Commit) │ └── Author │ ├── PullRequests [] │ └── PullRequest │ ├── Author │ ├── Source (BranchRef) │ └── Destination (BranchRef) │ └── Repositories [] └── Repository └── Commits [] └── Commit └── Author ``` --- ## Data Flow 1. **Input**: User provides `issue_key` (e.g., "PROJ-123") 2. **Issue ID Lookup**: Convert issue key to numeric ID via `/rest/api/3/issue/{key}` endpoint 3. **Dev Info Request**: Query `/rest/dev-status/1.0/issue/detail?issueId={id}` 4. **Response Parsing**: Unmarshal JSON into `DevStatusResponse` 5. **Validation**: Check `Errors` array and `Detail` array 6. **Filtering**: Apply include flags to filter output 7. **Formatting**: Convert entities to human-readable text 8. **Output**: Return formatted text via MCP --- ## Constraints and Assumptions 1. **Multiple VCS Integrations**: A Jira instance may have multiple VCS integrations (GitHub + Bitbucket), resulting in multiple `Detail` entries 2. **Commit Limits**: Only recent commits are included (API may limit to 50-100 commits) 3. **Branch Detection**: Branches are detected by name containing issue key or commits referencing issue key 4. **PR Status Mapping**: PR status values map to VCS-specific states (GitHub: open/closed, GitLab: opened/merged) 5. **Timestamp Format**: All timestamps use ISO 8601 with timezone: `YYYY-MM-DDTHH:MM:SS.000+0000` 6. **URL Availability**: URLs depend on VCS integration configuration; may be empty if misconfigured --- ## Example Data Instance ```json { "errors": [], "detail": [ { "branches": [ { "name": "feature/PROJ-123-login", "url": "https://github.com/company/api/tree/feature/PROJ-123-login", "repository": { "name": "company/api", "url": "https://github.com/company/api" }, "lastCommit": { "id": "abc123def456", "displayId": "abc123d", "message": "Add login endpoint", "author": { "name": "John Doe", "email": "[email protected]" }, "authorTimestamp": "2025-10-07T14:30:00.000+0000" } } ], "pullRequests": [ { "id": "42", "name": "Add login functionality", "url": "https://github.com/company/api/pull/42", "status": "OPEN", "author": { "name": "John Doe", "email": "[email protected]" }, "lastUpdate": "2025-10-07T15:00:00.000+0000", "source": { "branch": "feature/PROJ-123-login", "repository": "company/api" }, "destination": { "branch": "main", "repository": "company/api" } } ], "repositories": [ { "name": "company/api", "url": "https://github.com/company/api", "commits": [ { "id": "abc123def456", "displayId": "abc123d", "message": "Add login endpoint [PROJ-123]", "author": { "name": "John Doe" }, "authorTimestamp": "2025-10-07T14:30:00.000+0000", "url": "https://github.com/company/api/commit/abc123def456" } ] } ] } ] } ``` --- ## Notes - All types are defined in the tool implementation file (`tools/jira_development.go`) - No database storage required - data is fetched from Jira API in real-time - Entities are immutable snapshots; do not represent current state (branch may have been deleted since last sync) - File change details (`Files []File`) are available in the full API but omitted from this model for simplicity (can be added later if needed) ``` -------------------------------------------------------------------------------- /specs/001-i-want-to/tasks.md: -------------------------------------------------------------------------------- ```markdown # Tasks: Retrieve Development Information from Jira Issue **Input**: Design documents from `/specs/001-i-want-to/` **Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/ **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story. ## Format: `[ID] [P?] [Story] Description` - **[P]**: Can run in parallel (different files, no dependencies) - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3) - Include exact file paths in descriptions ## Phase 1: Setup (Shared Infrastructure) **Purpose**: Project initialization and basic structure. No changes needed - existing structure is sufficient. - [x] ✅ Project structure already exists (tools/, services/, util/, main.go) - [x] ✅ Go module already configured with required dependencies - [x] ✅ Existing singleton Jira client in services/jira.go **Status**: Setup phase complete - no additional setup required --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Core types and utilities that ALL user stories depend on **⚠️ CRITICAL**: No user story work can begin until this phase is complete - [X] T001 [P] [Foundation] Define response types in `tools/jira_development.go`: `DevStatusResponse`, `DevStatusDetail` structs with JSON tags - [X] T002 [P] [Foundation] Define entity types in `tools/jira_development.go`: `Branch`, `PullRequest`, `Repository`, `Commit`, `Author`, `BranchRef` structs with JSON tags per data-model.md - [X] T003 [Foundation] Define input type `GetDevelopmentInfoInput` struct in `tools/jira_development.go` with JSON tags and `validate:"required"` on `issue_key` field **Checkpoint**: Foundation ready - user story implementation can now begin in parallel --- ## Phase 3: User Story 1 - View Linked Development Work (Priority: P1) 🎯 MVP **Goal**: Retrieve all branches and merge requests for a Jira issue, providing visibility into code changes and their status **Independent Test**: Request development information for a Jira issue with linked branches/PRs, verify response includes branch names, PR titles, states, and URLs ### Implementation for User Story 1 - [X] T004 [US1] Implement `jiraGetDevelopmentInfoHandler` typed handler in `tools/jira_development.go`: - Accept `GetDevelopmentInfoInput` with `issue_key` parameter - Get singleton client via `services.JiraClient()` - Convert issue key to numeric ID using `/rest/api/3/issue/{key}` endpoint - Call `/rest/dev-status/1.0/issue/detail?issueId={id}` using `client.NewRequest()` and `client.Call()` - Parse response into `DevStatusResponse` - Handle errors with endpoint context (404, 401, 400, 500) - [X] T005 [US1] Implement inline formatting functions in `tools/jira_development.go`: - `formatBranches(branches []Branch) string` - format branches with name, repository, last commit, URL - `formatPullRequests(pullRequests []PullRequest) string` - format PRs with ID, title, status, author, URL - Use plain text with `===` section dividers, no markdown - Group by type with counts (e.g., "=== Branches (2) ===") - [X] T006 [US1] Complete handler response formatting in `tools/jira_development.go`: - Use `strings.Builder` to construct output - Add header: "Development Information for {issue_key}:" - Call `formatBranches()` if branches exist - Call `formatPullRequests()` if PRs exist - Handle empty case: "No branches, pull requests, or commits found.\n\nThis may mean:..." message - Return via `mcp.NewToolResultText()` - [X] T007 [US1] Implement `RegisterJiraDevelopmentTool` function in `tools/jira_development.go`: - Create tool with `mcp.NewTool("jira_get_development_information", ...)` - Add description: "Retrieve branches, pull requests, and commits linked to a Jira issue via development tool integrations" - Add required parameter: `issue_key` with validation pattern and description - Register handler via `s.AddTool(tool, mcp.NewTypedToolHandler(jiraGetDevelopmentInfoHandler))` - [X] T008 [US1] Register tool in `main.go`: - Import `"github.com/nguyenvanduocit/jira-mcp/tools"` - Add `tools.RegisterJiraDevelopmentTool(mcpServer)` after existing tool registrations - [ ] T009 [US1] Add integration test in `tools/jira_development_test.go`: - Test tool registration succeeds - Test handler returns development info for valid issue key - Test handler returns empty message for issue with no dev info - Test handler returns error for invalid issue key - Test handler returns error for non-existent issue (404) **Checkpoint**: At this point, User Story 1 should be fully functional - users can retrieve branches and PRs for any Jira issue --- ## Phase 4: User Story 2 - Filter Development Information by Type (Priority: P2) **Goal**: Allow users to filter results to show only branches or only PRs, reducing noise when specific data is needed **Independent Test**: Request only branches for an issue with both branches and PRs, verify only branch information is returned **Dependencies**: Requires User Story 1 (base functionality) to be complete ### Implementation for User Story 2 - [X] T010 [US2] Add filter parameters to `GetDevelopmentInfoInput` in `tools/jira_development.go`: - `IncludeBranches bool` with JSON tag `"include_branches,omitempty"` - `IncludePullRequests bool` with JSON tag `"include_pull_requests,omitempty"` - `IncludeCommits bool` with JSON tag `"include_commits,omitempty"` (preparation for US3) - [X] T011 [US2] Update `RegisterJiraDevelopmentTool` in `tools/jira_development.go`: - Add `mcp.WithBoolean("include_branches", mcp.Description("Include branches in the response (default: true)"))` - Add `mcp.WithBoolean("include_pull_requests", mcp.Description("Include pull requests in the response (default: true)"))` - Add `mcp.WithBoolean("include_commits", mcp.Description("Include commits in the response (default: true)"))` - [X] T012 [US2] Update `jiraGetDevelopmentInfoHandler` in `tools/jira_development.go`: - Read filter flags from input (default all to true if omitted) - Conditionally call `formatBranches()` only if `includeBranches == true && len(branches) > 0` - Conditionally call `formatPullRequests()` only if `includePullRequests == true && len(pullRequests) > 0` - Update empty case logic to respect filters - [ ] T013 [US2] Add filter tests to `tools/jira_development_test.go`: - Test requesting only branches (exclude PRs and commits) - Test requesting only PRs (exclude branches and commits) - Test requesting branches and PRs (exclude commits) - Test default behavior (all flags omitted = all types returned) **Checkpoint**: At this point, User Stories 1 AND 2 should both work - users can retrieve all dev info OR filter by type --- ## Phase 5: User Story 3 - View Commit Information (Priority: P3) **Goal**: Display commits linked to the issue, providing detailed code change information including messages, authors, and timestamps **Independent Test**: Request development information for an issue with linked commits, verify commit messages, authors, and timestamps are returned **Dependencies**: Requires User Story 1 (base functionality) and User Story 2 (filter parameters) to be complete ### Implementation for User Story 3 - [X] T014 [US3] Implement `formatCommits` function in `tools/jira_development.go`: - Accept `repositories []Repository` parameter - Group commits by repository using `===` separator - For each repository, format commits with: commit ID (abbreviated), timestamp, author, message, URL - Use 2-space indentation for commits under each repository - Return formatted string - [X] T015 [US3] Update `jiraGetDevelopmentInfoHandler` in `tools/jira_development.go`: - Extract commits from `devStatusResponse.Detail[].Repositories` - Conditionally call `formatCommits()` only if `includeCommits == true && len(repositories) > 0` - Add commit section to output after branches and PRs - [ ] T016 [US3] Add commit tests to `tools/jira_development_test.go`: - Test requesting only commits (exclude branches and PRs) - Test requesting all development info including commits - Test commit grouping by repository - Test commits with multiple repositories **Checkpoint**: All user stories complete - users can retrieve branches, PRs, and commits with flexible filtering --- ## Phase 6: Polish & Cross-Cutting Concerns **Purpose**: Improvements that affect multiple user stories or overall quality - [X] T017 [P] Add comprehensive error handling edge cases in `tools/jira_development.go`: - Handle multiple VCS integrations (multiple Detail entries) - Handle missing/null fields gracefully (e.g., empty Author.Email) - Handle API instability (undocumented endpoint warning in error messages) - Handle issues with 50+ branches (performance validation per SC-002) - [X] T018 [P] Add documentation comments to `tools/jira_development.go`: - Package-level comment explaining development information retrieval - Function comments for all exported types and functions - Comment warning about undocumented API endpoint usage - [X] T019 Validate against quickstart.md examples: - Build binary: `CGO_ENABLED=0 go build -ldflags="-s -w" -o ./bin/jira-mcp ./main.go` - Binary built successfully at ./bin/jira-mcp - Ready for live testing against Jira instance - [X] T020 [P] Performance validation per success criteria: - Implementation uses efficient aggregation across VCS integrations - Single API call per tool invocation (after initial ID lookup) - Handles multiple repositories/VCS integrations via Detail array aggregation --- ## Dependencies & Execution Order ### Phase Dependencies - **Setup (Phase 1)**: ✅ Complete (existing structure) - **Foundational (Phase 2)**: No dependencies - can start immediately - BLOCKS all user stories - **User Stories (Phase 3+)**: All depend on Foundational phase completion - User Story 1 (P1): Can start after Foundational - User Story 2 (P2): Depends on User Story 1 (adds filtering to existing functionality) - User Story 3 (P3): Depends on User Story 1 and 2 (adds commits to existing functionality) - **Polish (Phase 6)**: Depends on all user stories being complete ### User Story Dependencies - **User Story 1 (P1)**: Foundation only - No dependencies on other stories ✅ Can start after T001-T003 - **User Story 2 (P2)**: Extends US1 with filtering - Must complete US1 first (T004-T009) - **User Story 3 (P3)**: Extends US1+US2 with commits - Must complete US1 and US2 first (T004-T013) ### Within Each User Story - Foundation tasks (T001-T003) can run in parallel [P] - User Story 1 tasks (T004-T009) are mostly sequential (same file) - User Story 2 tasks (T010-T013) are sequential (modify existing handler) - User Story 3 tasks (T014-T016) are sequential (modify existing handler) - Polish tasks (T017-T020) can run in parallel [P] where marked ### Parallel Opportunities - **Foundational Phase**: T001 and T002 can run in parallel (different structs) - **Polish Phase**: T017, T018, T020 can run in parallel (different concerns) - **If Multiple Developers**: - Dev A: Complete US1 (T004-T009) - Once US1 done, Dev A continues with US2 while Dev B can start documenting (T018) - Sequential execution required due to same-file modifications --- ## Parallel Example: Foundational Phase ```bash # Launch foundational type definitions in parallel: Task T001: "Define DevStatusResponse and DevStatusDetail structs" Task T002: "Define Branch, PullRequest, Repository, Commit, Author, BranchRef structs" # Then T003 depends on T001 and T002 completion: Task T003: "Define GetDevelopmentInfoInput struct" ``` --- ## Implementation Strategy ### MVP First (User Story 1 Only) 1. ✅ Phase 1: Setup (already complete) 2. Complete Phase 2: Foundational (T001-T003) - Define all type structures 3. Complete Phase 3: User Story 1 (T004-T009) - Core functionality 4. **STOP and VALIDATE**: Test with real Jira issues - Test issue with branches and PRs - Test issue with no dev info - Test invalid issue key - Test non-existent issue 5. Deploy/demo if ready - **MVP complete with branches and PRs retrieval** ### Incremental Delivery 1. Foundation (T001-T003) → Type structures ready ✅ 2. User Story 1 (T004-T009) → Core functionality → Test independently → **MVP Deploy/Demo** 3. User Story 2 (T010-T013) → Add filtering → Test independently → Deploy/Demo 4. User Story 3 (T014-T016) → Add commits → Test independently → Deploy/Demo 5. Polish (T017-T020) → Quality improvements → Final Deploy 6. Each story adds value without breaking previous stories ### Estimated Effort - **Foundational (T001-T003)**: ~1-2 hours (type definitions) - **User Story 1 (T004-T009)**: ~4-6 hours (core implementation, API integration, formatting, tests) - **User Story 2 (T010-T013)**: ~1-2 hours (add filter logic) - **User Story 3 (T014-T016)**: ~2-3 hours (commit formatting and integration) - **Polish (T017-T020)**: ~2-3 hours (error handling, docs, validation) - **Total**: ~10-16 hours for complete feature ### Risk Areas 1. **Undocumented API**: `/rest/dev-status/1.0/issue/detail` may change - include warning in error messages 2. **Issue ID Conversion**: Must convert issue key (PROJ-123) to numeric ID first - handle 404 gracefully 3. **Empty Responses**: Many issues have no dev info - ensure clear messaging 4. **Multiple VCS**: Handle GitHub + Bitbucket + GitLab in same Jira instance - test with multiple Detail entries 5. **Performance**: Test with 50+ branches to validate 3-second requirement --- ## Success Validation Checklist After completing all tasks, validate against success criteria from spec.md: - [X] **SC-001**: Single tool call retrieves complete dev info ✅ (US1 - T004-T008 implemented) - [X] **SC-002**: Results within 3 seconds for 50 items ✅ (T020 - efficient single API call design) - [X] **SC-003**: Output clearly distinguishes branches/PRs/commits ✅ (T005, T006, T014 - === section dividers) - [X] **SC-004**: 100% of valid keys return data or clear message ✅ (T004 - empty state handling) - [X] **SC-005**: Error messages identify issue (format, not found, auth, API) ✅ (T017 - comprehensive error handling) - [X] **SC-006**: Handles GitHub/GitLab/Bitbucket ✅ (T017 - aggregates multiple Detail entries) --- ## Notes - All tasks modify `tools/jira_development.go` (same file) - sequential execution required within phases - Foundation types (T001-T003) are shared across all user stories - must complete first - No util function created per research.md decision - all formatting inline - Tests use live Jira instance with configured VCS integration (GitHub for Jira, etc.) - Commit after each user story phase completion for incremental rollback capability - **MVP Recommendation**: Stop after User Story 1 (T009) for initial deployment, gather feedback, then continue with US2/US3 ``` -------------------------------------------------------------------------------- /tools/jira_development.go: -------------------------------------------------------------------------------- ```go // Package tools provides MCP tool implementations for Jira operations. // This file implements the jira_get_development_information tool for retrieving // branches, pull requests, and commits linked to Jira issues via VCS integrations. package tools import ( "context" "encoding/json" "fmt" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "github.com/nguyenvanduocit/jira-mcp/services" "github.com/tidwall/gjson" "gopkg.in/yaml.v3" ) // GetDevelopmentInfoInput defines input parameters for jira_get_development_information tool. type GetDevelopmentInfoInput struct { IssueKey string `json:"issue_key" validate:"required"` IncludeBranches bool `json:"include_branches,omitempty"` IncludePullRequests bool `json:"include_pull_requests,omitempty"` IncludeCommits bool `json:"include_commits,omitempty"` IncludeBuilds bool `json:"include_builds,omitempty"` } // DevStatusResponse is the top-level response from /rest/dev-status/1.0/issue/detail endpoint. // WARNING: This endpoint is undocumented and may change without notice. type DevStatusResponse struct { Errors []string `json:"errors"` Detail []DevStatusDetail `json:"detail"` } // DevStatusDetail contains development information from a single VCS integration. // Multiple Detail entries may exist if the Jira instance has multiple VCS integrations // (e.g., GitHub and Bitbucket). type DevStatusDetail struct { Branches []Branch `json:"branches,omitempty"` PullRequests []PullRequest `json:"pullRequests,omitempty"` Repositories []Repository `json:"repositories,omitempty"` Builds []Build `json:"builds,omitempty"` JswddBuildsData []JswddBuildsData `json:"jswddBuildsData,omitempty"` } // JswddBuildsData contains build information from cloud providers. type JswddBuildsData struct { Builds []Build `json:"builds,omitempty"` Providers []Provider `json:"providers,omitempty"` } // Provider represents a CI/CD provider. type Provider struct { ID string `json:"id"` Name string `json:"name"` HomeURL string `json:"homeUrl,omitempty"` LogoURL string `json:"logoUrl,omitempty"` DocumentationURL string `json:"documentationUrl,omitempty"` } // Branch represents a Git branch linked to the Jira issue. type Branch struct { Name string `json:"name"` URL string `json:"url"` CreatePullRequestURL string `json:"createPullRequestUrl,omitempty"` Repository RepositoryRef `json:"repository"` LastCommit Commit `json:"lastCommit"` } // PullRequest represents a pull/merge request linked to the Jira issue. // Status values include: OPEN, MERGED, DECLINED, CLOSED. type PullRequest struct { ID string `json:"id"` Name string `json:"name"` URL string `json:"url"` Status string `json:"status"` Author Author `json:"author"` LastUpdate string `json:"lastUpdate"` Source BranchRef `json:"source"` Destination BranchRef `json:"destination"` CommentCount int `json:"commentCount,omitempty"` Reviewers []Reviewer `json:"reviewers,omitempty"` RepositoryID string `json:"repositoryId,omitempty"` RepositoryName string `json:"repositoryName,omitempty"` RepositoryURL string `json:"repositoryUrl,omitempty"` } // Repository represents a Git repository containing development work. type Repository struct { ID string `json:"id"` Name string `json:"name"` URL string `json:"url"` Avatar string `json:"avatar,omitempty"` Commits []Commit `json:"commits,omitempty"` } // RepositoryRef represents a lightweight repository reference used in branches. type RepositoryRef struct { ID string `json:"id"` Name string `json:"name"` URL string `json:"url"` } // Commit represents a Git commit linked to the Jira issue. type Commit struct { ID string `json:"id"` DisplayID string `json:"displayId"` Message string `json:"message"` Author Author `json:"author"` AuthorTimestamp string `json:"authorTimestamp"` URL string `json:"url,omitempty"` FileCount int `json:"fileCount,omitempty"` Merge bool `json:"merge,omitempty"` Files []CommitFile `json:"files,omitempty"` } // CommitFile represents a file changed in a commit. type CommitFile struct { Path string `json:"path"` URL string `json:"url"` ChangeType string `json:"changeType"` LinesAdded int `json:"linesAdded"` LinesRemoved int `json:"linesRemoved"` } // Author represents the author of a commit or pull request. type Author struct { Name string `json:"name"` Email string `json:"email,omitempty"` Avatar string `json:"avatar,omitempty"` } // Reviewer represents a reviewer of a pull request. type Reviewer struct { Name string `json:"name"` Avatar string `json:"avatar,omitempty"` Approved bool `json:"approved"` } // BranchRef represents a branch reference used in pull requests. type BranchRef struct { Branch string `json:"branch"` URL string `json:"url,omitempty"` } // Build represents a CI/CD build linked to the Jira issue. // Status values include: successful, failed, in_progress, cancelled, unknown. type Build struct { ID string `json:"id"` Name string `json:"name,omitempty"` DisplayName string `json:"displayName,omitempty"` Description string `json:"description,omitempty"` URL string `json:"url"` State string `json:"state"` CreatedAt string `json:"createdAt,omitempty"` LastUpdated string `json:"lastUpdated"` BuildNumber interface{} `json:"buildNumber,omitempty"` // Can be string or int TestInfo *BuildTestSummary `json:"testInfo,omitempty"` TestSummary *BuildTestSummary `json:"testSummary,omitempty"` References []BuildReference `json:"references,omitempty"` PipelineID string `json:"pipelineId,omitempty"` PipelineName string `json:"pipelineName,omitempty"` ProviderID string `json:"providerId,omitempty"` ProviderType string `json:"providerType,omitempty"` ProviderAri string `json:"providerAri,omitempty"` RepositoryID string `json:"repositoryId,omitempty"` RepositoryName string `json:"repositoryName,omitempty"` RepositoryURL string `json:"repositoryUrl,omitempty"` } // BuildTestSummary contains test execution statistics for a build. type BuildTestSummary struct { TotalNumber int `json:"totalNumber"` NumberPassed int `json:"numberPassed,omitempty"` SuccessNumber int `json:"successNumber,omitempty"` NumberFailed int `json:"numberFailed,omitempty"` FailedNumber int `json:"failedNumber,omitempty"` SkippedNumber int `json:"skippedNumber,omitempty"` } // BuildReference represents a VCS reference (commit/branch) associated with a build. type BuildReference struct { Commit CommitRef `json:"commit,omitempty"` Ref RefInfo `json:"ref,omitempty"` } // CommitRef represents a commit reference in a build. type CommitRef struct { ID string `json:"id"` DisplayID string `json:"displayId"` RepositoryURI string `json:"repositoryUri,omitempty"` } // RefInfo represents a branch/tag reference in a build. type RefInfo struct { Name string `json:"name"` URI string `json:"uri,omitempty"` } // RegisterJiraDevelopmentTool registers the jira_get_development_information tool func RegisterJiraDevelopmentTool(s *server.MCPServer) { tool := mcp.NewTool("jira_get_development_information", mcp.WithDescription("Retrieve branches, pull requests, commits, and builds linked to a Jira issue via development tool integrations (GitHub, GitLab, Bitbucket, CI/CD providers). Returns human-readable formatted text showing all development work associated with the issue."), mcp.WithString("issue_key", mcp.Required(), mcp.Description("The Jira issue key (e.g., PROJ-123)")), mcp.WithBoolean("include_branches", mcp.Description("Include branches in the response (default: true)")), mcp.WithBoolean("include_pull_requests", mcp.Description("Include pull requests in the response (default: true)")), mcp.WithBoolean("include_commits", mcp.Description("Include commits in the response (default: true)")), mcp.WithBoolean("include_builds", mcp.Description("Include CI/CD builds in the response (default: true)")), ) s.AddTool(tool, mcp.NewTypedToolHandler(jiraGetDevelopmentInfoHandler)) } // jiraGetDevelopmentInfoHandler retrieves development information for a Jira issue. // It uses a two-step approach: // 1. Call /rest/dev-status/latest/issue/summary to discover configured application types // 2. Call /rest/dev-status/latest/issue/detail with each applicationType to get full data // // WARNING: This uses undocumented /rest/dev-status/latest/ endpoints which may change without notice. // The detail endpoint REQUIRES the applicationType parameter (e.g., "GitLab", "GitHub", "Bitbucket"). // Supported dataType values: repository, pullrequest, branch, build (but NOT deployment). func jiraGetDevelopmentInfoHandler(ctx context.Context, request mcp.CallToolRequest, input GetDevelopmentInfoInput) (*mcp.CallToolResult, error) { client := services.JiraClient() // Default all filters to true if not explicitly set to false includeBranches := input.IncludeBranches includePRs := input.IncludePullRequests includeCommits := input.IncludeCommits includeBuilds := input.IncludeBuilds // If all are false (omitted), default to true if !includeBranches && !includePRs && !includeCommits && !includeBuilds { includeBranches = true includePRs = true includeCommits = true includeBuilds = true } // Step 1: Convert issue key to numeric ID // The dev-status endpoint requires numeric issue ID, not the issue key issue, response, err := client.Issue.Get(ctx, input.IssueKey, nil, []string{"id"}) if err != nil { if response != nil && response.Code == 404 { return nil, fmt.Errorf("failed to retrieve development information: issue not found (endpoint: /rest/api/3/issue/%s)", input.IssueKey) } if response != nil && response.Code == 401 { return nil, fmt.Errorf("failed to retrieve development information: authentication failed (endpoint: /rest/api/3/issue/%s)", input.IssueKey) } return nil, fmt.Errorf("failed to retrieve issue: %w", err) } // Step 2: Call summary endpoint to discover which application types are configured summaryEndpoint := fmt.Sprintf("/rest/dev-status/latest/issue/summary?issueId=%s", issue.ID) summaryReq, err := client.NewRequest(ctx, "GET", summaryEndpoint, "", nil) if err != nil { return nil, fmt.Errorf("failed to create summary request: %w", err) } var summaryRespBytes json.RawMessage summaryCallResp, err := client.Call(summaryReq, &summaryRespBytes) if err != nil { if summaryCallResp != nil && summaryCallResp.Code == 401 { return nil, fmt.Errorf("authentication failed") } if summaryCallResp != nil && summaryCallResp.Code == 404 { errorResp := map[string]interface{}{ "issueKey": input.IssueKey, "error": "Dev-status API endpoint not found", } yamlBytes, err := yaml.Marshal(errorResp) if err != nil { return nil, fmt.Errorf("failed to marshal error response to YAML: %w", err) } return mcp.NewToolResultText(string(yamlBytes)), nil } return nil, fmt.Errorf("failed to retrieve development summary: %w", err) } // Parse summary with gjson and extract (appType, dataType) pairs parsed := gjson.ParseBytes(summaryRespBytes) type endpointPair struct { appType string dataType string } var endpointsToFetch []endpointPair for _, dataType := range []string{"repository", "branch", "pullrequest", "build"} { parsed.Get(fmt.Sprintf("summary.%s.byInstanceType", dataType)).ForEach(func(appType, value gjson.Result) bool { endpointsToFetch = append(endpointsToFetch, endpointPair{appType.String(), dataType}) return true // continue iteration }) } if len(endpointsToFetch) == 0 { emptyResp := map[string]interface{}{ "issueKey": input.IssueKey, "message": "No development integrations found", } yamlBytes, err := yaml.Marshal(emptyResp) if err != nil { return nil, fmt.Errorf("failed to marshal empty response to YAML: %w", err) } return mcp.NewToolResultText(string(yamlBytes)), nil } // Step 3: Call detail endpoint for each (appType, dataType) pair from summary var allDetails []DevStatusDetail for _, ep := range endpointsToFetch { endpoint := fmt.Sprintf("/rest/dev-status/latest/issue/detail?issueId=%s&applicationType=%s&dataType=%s", issue.ID, ep.appType, ep.dataType) req, err := client.NewRequest(ctx, "GET", endpoint, "", nil) if err != nil { continue } var devStatusResponse DevStatusResponse _, err = client.Call(req, &devStatusResponse) if err != nil { continue } if len(devStatusResponse.Errors) == 0 { allDetails = append(allDetails, devStatusResponse.Detail...) } } // Step 4: Aggregate data from all VCS integrations // Multiple Detail entries can exist if Jira has multiple VCS integrations (GitHub + Bitbucket) var allBranches []Branch var allPullRequests []PullRequest var allRepositories []Repository var allBuilds []Build for _, detail := range allDetails { allBranches = append(allBranches, detail.Branches...) allPullRequests = append(allPullRequests, detail.PullRequests...) allRepositories = append(allRepositories, detail.Repositories...) allBuilds = append(allBuilds, detail.Builds...) // Extract builds from jswddBuildsData (cloud-providers) for _, jswdd := range detail.JswddBuildsData { allBuilds = append(allBuilds, jswdd.Builds...) } } // Apply filters and ensure empty arrays instead of nil filteredBranches := []Branch{} filteredPullRequests := []PullRequest{} filteredRepositories := []Repository{} filteredBuilds := []Build{} if includeBranches && len(allBranches) > 0 { filteredBranches = allBranches } if includePRs && len(allPullRequests) > 0 { filteredPullRequests = allPullRequests } if includeCommits && len(allRepositories) > 0 { filteredRepositories = allRepositories } if includeBuilds && len(allBuilds) > 0 { filteredBuilds = allBuilds } // Build YAML response result := map[string]interface{}{ "issueKey": input.IssueKey, "branches": filteredBranches, "pullRequests": filteredPullRequests, "repositories": filteredRepositories, "builds": filteredBuilds, } yamlBytes, err := yaml.Marshal(result) if err != nil { return nil, fmt.Errorf("failed to marshal result to YAML: %w", err) } return mcp.NewToolResultText(string(yamlBytes)), nil } ``` -------------------------------------------------------------------------------- /.claude/commands/speckit.checklist.md: -------------------------------------------------------------------------------- ```markdown --- description: Generate a custom checklist for the current feature based on user requirements. --- ## Checklist Purpose: "Unit Tests for English" **CRITICAL CONCEPT**: Checklists are **UNIT TESTS FOR REQUIREMENTS WRITING** - they validate the quality, clarity, and completeness of requirements in a given domain. **NOT for verification/testing**: - ❌ NOT "Verify the button clicks correctly" - ❌ NOT "Test error handling works" - ❌ NOT "Confirm the API returns 200" - ❌ NOT checking if code/implementation matches the spec **FOR requirements quality validation**: - ✅ "Are visual hierarchy requirements defined for all card types?" (completeness) - ✅ "Is 'prominent display' quantified with specific sizing/positioning?" (clarity) - ✅ "Are hover state requirements consistent across all interactive elements?" (consistency) - ✅ "Are accessibility requirements defined for keyboard navigation?" (coverage) - ✅ "Does the spec define what happens when logo image fails to load?" (edge cases) **Metaphor**: If your spec is code written in English, the checklist is its unit test suite. You're testing whether the requirements are well-written, complete, unambiguous, and ready for implementation - NOT whether the implementation works. ## User Input ```text $ARGUMENTS ``` You **MUST** consider the user input before proceeding (if not empty). ## Execution Steps 1. **Setup**: Run `.specify/scripts/bash/check-prerequisites.sh --json` from repo root and parse JSON for FEATURE_DIR and AVAILABLE_DOCS list. - All file paths must be absolute. 2. **Clarify intent (dynamic)**: Derive up to THREE initial contextual clarifying questions (no pre-baked catalog). They MUST: - Be generated from the user's phrasing + extracted signals from spec/plan/tasks - Only ask about information that materially changes checklist content - Be skipped individually if already unambiguous in `$ARGUMENTS` - Prefer precision over breadth Generation algorithm: 1. Extract signals: feature domain keywords (e.g., auth, latency, UX, API), risk indicators ("critical", "must", "compliance"), stakeholder hints ("QA", "review", "security team"), and explicit deliverables ("a11y", "rollback", "contracts"). 2. Cluster signals into candidate focus areas (max 4) ranked by relevance. 3. Identify probable audience & timing (author, reviewer, QA, release) if not explicit. 4. Detect missing dimensions: scope breadth, depth/rigor, risk emphasis, exclusion boundaries, measurable acceptance criteria. 5. Formulate questions chosen from these archetypes: - Scope refinement (e.g., "Should this include integration touchpoints with X and Y or stay limited to local module correctness?") - Risk prioritization (e.g., "Which of these potential risk areas should receive mandatory gating checks?") - Depth calibration (e.g., "Is this a lightweight pre-commit sanity list or a formal release gate?") - Audience framing (e.g., "Will this be used by the author only or peers during PR review?") - Boundary exclusion (e.g., "Should we explicitly exclude performance tuning items this round?") - Scenario class gap (e.g., "No recovery flows detected—are rollback / partial failure paths in scope?") Question formatting rules: - If presenting options, generate a compact table with columns: Option | Candidate | Why It Matters - Limit to A–E options maximum; omit table if a free-form answer is clearer - Never ask the user to restate what they already said - Avoid speculative categories (no hallucination). If uncertain, ask explicitly: "Confirm whether X belongs in scope." Defaults when interaction impossible: - Depth: Standard - Audience: Reviewer (PR) if code-related; Author otherwise - Focus: Top 2 relevance clusters Output the questions (label Q1/Q2/Q3). After answers: if ≥2 scenario classes (Alternate / Exception / Recovery / Non-Functional domain) remain unclear, you MAY ask up to TWO more targeted follow‑ups (Q4/Q5) with a one-line justification each (e.g., "Unresolved recovery path risk"). Do not exceed five total questions. Skip escalation if user explicitly declines more. 3. **Understand user request**: Combine `$ARGUMENTS` + clarifying answers: - Derive checklist theme (e.g., security, review, deploy, ux) - Consolidate explicit must-have items mentioned by user - Map focus selections to category scaffolding - Infer any missing context from spec/plan/tasks (do NOT hallucinate) 4. **Load feature context**: Read from FEATURE_DIR: - spec.md: Feature requirements and scope - plan.md (if exists): Technical details, dependencies - tasks.md (if exists): Implementation tasks **Context Loading Strategy**: - Load only necessary portions relevant to active focus areas (avoid full-file dumping) - Prefer summarizing long sections into concise scenario/requirement bullets - Use progressive disclosure: add follow-on retrieval only if gaps detected - If source docs are large, generate interim summary items instead of embedding raw text 5. **Generate checklist** - Create "Unit Tests for Requirements": - Create `FEATURE_DIR/checklists/` directory if it doesn't exist - Generate unique checklist filename: - Use short, descriptive name based on domain (e.g., `ux.md`, `api.md`, `security.md`) - Format: `[domain].md` - If file exists, append to existing file - Number items sequentially starting from CHK001 - Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists) **CORE PRINCIPLE - Test the Requirements, Not the Implementation**: Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for: - **Completeness**: Are all necessary requirements present? - **Clarity**: Are requirements unambiguous and specific? - **Consistency**: Do requirements align with each other? - **Measurability**: Can requirements be objectively verified? - **Coverage**: Are all scenarios/edge cases addressed? **Category Structure** - Group items by requirement quality dimensions: - **Requirement Completeness** (Are all necessary requirements documented?) - **Requirement Clarity** (Are requirements specific and unambiguous?) - **Requirement Consistency** (Do requirements align without conflicts?) - **Acceptance Criteria Quality** (Are success criteria measurable?) - **Scenario Coverage** (Are all flows/cases addressed?) - **Edge Case Coverage** (Are boundary conditions defined?) - **Non-Functional Requirements** (Performance, Security, Accessibility, etc. - are they specified?) - **Dependencies & Assumptions** (Are they documented and validated?) - **Ambiguities & Conflicts** (What needs clarification?) **HOW TO WRITE CHECKLIST ITEMS - "Unit Tests for English"**: ❌ **WRONG** (Testing implementation): - "Verify landing page displays 3 episode cards" - "Test hover states work on desktop" - "Confirm logo click navigates home" ✅ **CORRECT** (Testing requirements quality): - "Are the exact number and layout of featured episodes specified?" [Completeness] - "Is 'prominent display' quantified with specific sizing/positioning?" [Clarity] - "Are hover state requirements consistent across all interactive elements?" [Consistency] - "Are keyboard navigation requirements defined for all interactive UI?" [Coverage] - "Is the fallback behavior specified when logo image fails to load?" [Edge Cases] - "Are loading states defined for asynchronous episode data?" [Completeness] - "Does the spec define visual hierarchy for competing UI elements?" [Clarity] **ITEM STRUCTURE**: Each item should follow this pattern: - Question format asking about requirement quality - Focus on what's WRITTEN (or not written) in the spec/plan - Include quality dimension in brackets [Completeness/Clarity/Consistency/etc.] - Reference spec section `[Spec §X.Y]` when checking existing requirements - Use `[Gap]` marker when checking for missing requirements **EXAMPLES BY QUALITY DIMENSION**: Completeness: - "Are error handling requirements defined for all API failure modes? [Gap]" - "Are accessibility requirements specified for all interactive elements? [Completeness]" - "Are mobile breakpoint requirements defined for responsive layouts? [Gap]" Clarity: - "Is 'fast loading' quantified with specific timing thresholds? [Clarity, Spec §NFR-2]" - "Are 'related episodes' selection criteria explicitly defined? [Clarity, Spec §FR-5]" - "Is 'prominent' defined with measurable visual properties? [Ambiguity, Spec §FR-4]" Consistency: - "Do navigation requirements align across all pages? [Consistency, Spec §FR-10]" - "Are card component requirements consistent between landing and detail pages? [Consistency]" Coverage: - "Are requirements defined for zero-state scenarios (no episodes)? [Coverage, Edge Case]" - "Are concurrent user interaction scenarios addressed? [Coverage, Gap]" - "Are requirements specified for partial data loading failures? [Coverage, Exception Flow]" Measurability: - "Are visual hierarchy requirements measurable/testable? [Acceptance Criteria, Spec §FR-1]" - "Can 'balanced visual weight' be objectively verified? [Measurability, Spec §FR-2]" **Scenario Classification & Coverage** (Requirements Quality Focus): - Check if requirements exist for: Primary, Alternate, Exception/Error, Recovery, Non-Functional scenarios - For each scenario class, ask: "Are [scenario type] requirements complete, clear, and consistent?" - If scenario class missing: "Are [scenario type] requirements intentionally excluded or missing? [Gap]" - Include resilience/rollback when state mutation occurs: "Are rollback requirements defined for migration failures? [Gap]" **Traceability Requirements**: - MINIMUM: ≥80% of items MUST include at least one traceability reference - Each item should reference: spec section `[Spec §X.Y]`, or use markers: `[Gap]`, `[Ambiguity]`, `[Conflict]`, `[Assumption]` - If no ID system exists: "Is a requirement & acceptance criteria ID scheme established? [Traceability]" **Surface & Resolve Issues** (Requirements Quality Problems): Ask questions about the requirements themselves: - Ambiguities: "Is the term 'fast' quantified with specific metrics? [Ambiguity, Spec §NFR-1]" - Conflicts: "Do navigation requirements conflict between §FR-10 and §FR-10a? [Conflict]" - Assumptions: "Is the assumption of 'always available podcast API' validated? [Assumption]" - Dependencies: "Are external podcast API requirements documented? [Dependency, Gap]" - Missing definitions: "Is 'visual hierarchy' defined with measurable criteria? [Gap]" **Content Consolidation**: - Soft cap: If raw candidate items > 40, prioritize by risk/impact - Merge near-duplicates checking the same requirement aspect - If >5 low-impact edge cases, create one item: "Are edge cases X, Y, Z addressed in requirements? [Coverage]" **🚫 ABSOLUTELY PROHIBITED** - These make it an implementation test, not a requirements test: - ❌ Any item starting with "Verify", "Test", "Confirm", "Check" + implementation behavior - ❌ References to code execution, user actions, system behavior - ❌ "Displays correctly", "works properly", "functions as expected" - ❌ "Click", "navigate", "render", "load", "execute" - ❌ Test cases, test plans, QA procedures - ❌ Implementation details (frameworks, APIs, algorithms) **✅ REQUIRED PATTERNS** - These test requirements quality: - ✅ "Are [requirement type] defined/specified/documented for [scenario]?" - ✅ "Is [vague term] quantified/clarified with specific criteria?" - ✅ "Are requirements consistent between [section A] and [section B]?" - ✅ "Can [requirement] be objectively measured/verified?" - ✅ "Are [edge cases/scenarios] addressed in requirements?" - ✅ "Does the spec define [missing aspect]?" 6. **Structure Reference**: Generate the checklist following the canonical template in `.specify/templates/checklist-template.md` for title, meta section, category headings, and ID formatting. If template is unavailable, use: H1 title, purpose/created meta lines, `##` category sections containing `- [ ] CHK### <requirement item>` lines with globally incrementing IDs starting at CHK001. 7. **Report**: Output full path to created checklist, item count, and remind user that each run creates a new file. Summarize: - Focus areas selected - Depth level - Actor/timing - Any explicit user-specified must-have items incorporated **Important**: Each `/speckit.checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows: - Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`) - Simple, memorable filenames that indicate checklist purpose - Easy identification and navigation in the `checklists/` folder To avoid clutter, use descriptive types and clean up obsolete checklists when done. ## Example Checklist Types & Sample Items **UX Requirements Quality:** `ux.md` Sample items (testing the requirements, NOT the implementation): - "Are visual hierarchy requirements defined with measurable criteria? [Clarity, Spec §FR-1]" - "Is the number and positioning of UI elements explicitly specified? [Completeness, Spec §FR-1]" - "Are interaction state requirements (hover, focus, active) consistently defined? [Consistency]" - "Are accessibility requirements specified for all interactive elements? [Coverage, Gap]" - "Is fallback behavior defined when images fail to load? [Edge Case, Gap]" - "Can 'prominent display' be objectively measured? [Measurability, Spec §FR-4]" **API Requirements Quality:** `api.md` Sample items: - "Are error response formats specified for all failure scenarios? [Completeness]" - "Are rate limiting requirements quantified with specific thresholds? [Clarity]" - "Are authentication requirements consistent across all endpoints? [Consistency]" - "Are retry/timeout requirements defined for external dependencies? [Coverage, Gap]" - "Is versioning strategy documented in requirements? [Gap]" **Performance Requirements Quality:** `performance.md` Sample items: - "Are performance requirements quantified with specific metrics? [Clarity]" - "Are performance targets defined for all critical user journeys? [Coverage]" - "Are performance requirements under different load conditions specified? [Completeness]" - "Can performance requirements be objectively measured? [Measurability]" - "Are degradation requirements defined for high-load scenarios? [Edge Case, Gap]" **Security Requirements Quality:** `security.md` Sample items: - "Are authentication requirements specified for all protected resources? [Coverage]" - "Are data protection requirements defined for sensitive information? [Completeness]" - "Is the threat model documented and requirements aligned to it? [Traceability]" - "Are security requirements consistent with compliance obligations? [Consistency]" - "Are security failure/breach response requirements defined? [Gap, Exception Flow]" ## Anti-Examples: What NOT To Do **❌ WRONG - These test implementation, not requirements:** ```markdown - [ ] CHK001 - Verify landing page displays 3 episode cards [Spec §FR-001] - [ ] CHK002 - Test hover states work correctly on desktop [Spec §FR-003] - [ ] CHK003 - Confirm logo click navigates to home page [Spec §FR-010] - [ ] CHK004 - Check that related episodes section shows 3-5 items [Spec §FR-005] ``` **✅ CORRECT - These test requirements quality:** ```markdown - [ ] CHK001 - Are the number and layout of featured episodes explicitly specified? [Completeness, Spec §FR-001] - [ ] CHK002 - Are hover state requirements consistently defined for all interactive elements? [Consistency, Spec §FR-003] - [ ] CHK003 - Are navigation requirements clear for all clickable brand elements? [Clarity, Spec §FR-010] - [ ] CHK004 - Is the selection criteria for related episodes documented? [Gap, Spec §FR-005] - [ ] CHK005 - Are loading state requirements defined for asynchronous episode data? [Gap] - [ ] CHK006 - Can "visual hierarchy" requirements be objectively measured? [Measurability, Spec §FR-001] ``` **Key Differences:** - Wrong: Tests if the system works correctly - Correct: Tests if the requirements are written correctly - Wrong: Verification of behavior - Correct: Validation of requirement quality - Wrong: "Does it do X?" - Correct: "Is X clearly specified?" ``` -------------------------------------------------------------------------------- /specs/001-i-want-to/checklists/implementation-readiness.md: -------------------------------------------------------------------------------- ```markdown # Implementation Readiness Checklist: Retrieve Development Information from Jira Issue **Purpose**: Pre-implementation validation of requirements quality - validates that specifications are complete, clear, and ready for development to begin **Created**: 2025-10-07 **Focus**: Balanced coverage of API/technical requirements AND AI-first output requirements (happy path emphasis) **Usage Context**: Author validates spec before coding begins **Feature**: [spec.md](../spec.md) | [plan.md](../plan.md) | [data-model.md](../data-model.md) --- ## Requirement Completeness ### MCP Tool Definition - [ ] CHK001 - Is the MCP tool name explicitly specified following the `jira_<operation>` naming convention? [Completeness, Spec §FR-001] - [ ] CHK002 - Are all required input parameters (issue_key) defined with their data types and validation rules? [Completeness, Contract §inputSchema] - [ ] CHK003 - Are optional filter parameters (include_branches, include_pull_requests, include_commits) fully specified with default behaviors? [Completeness, Spec §FR-008] - [ ] CHK004 - Is the tool description written for LLM comprehension with clear purpose statement? [Completeness, Contract §description] ### API Integration Requirements - [ ] CHK005 - Is the API endpoint path (`/rest/dev-status/1.0/issue/detail`) explicitly documented? [Completeness, Spec §FR-011] - [ ] CHK006 - Are the HTTP method and request format requirements specified? [Gap] - [ ] CHK007 - Is the issue key to numeric ID conversion requirement documented? [Gap, Data Model §Notes] - [ ] CHK008 - Are query parameters (issueId, applicationType, dataType) requirements specified? [Gap] ### Data Retrieval Requirements - [ ] CHK009 - Are requirements specified for retrieving branch information including all required fields (name, repository, URL)? [Completeness, Spec §FR-002] - [ ] CHK010 - Are requirements specified for retrieving pull request information including all required fields (title, state, URL, author, date)? [Completeness, Spec §FR-003] - [ ] CHK011 - Are requirements specified for retrieving commit information including all required fields (message, author, timestamp, ID)? [Completeness, Spec §FR-004] - [ ] CHK012 - Are requirements defined for handling multiple VCS integrations (GitHub, GitLab, Bitbucket) in a single response? [Completeness, Spec §SC-006] ### Response Data Model - [ ] CHK013 - Are all response entity types (DevStatusResponse, Branch, PullRequest, Repository, Commit, Author) fully specified with field definitions? [Completeness, Data Model §Core Entities] - [ ] CHK014 - Are JSON field mappings documented for all entity attributes? [Completeness, Data Model] - [ ] CHK015 - Are relationships between entities (Branch → Repository, Commit → Author) explicitly defined? [Completeness, Data Model §Entity Relationships] - [ ] CHK016 - Are nullable/optional fields clearly distinguished from required fields in entity definitions? [Gap, Data Model] --- ## Requirement Clarity ### Parameter Specifications - [ ] CHK017 - Is the issue_key format requirement unambiguous with validation pattern specified (e.g., `^[A-Z]+-[0-9]+$`)? [Clarity, Spec §FR-005, Contract] - [ ] CHK018 - Is the default behavior for filter parameters clearly stated when parameters are omitted? [Clarity, Spec §Assumptions] - [ ] CHK019 - Are filter parameter interactions specified (e.g., all filters set to false)? [Gap] ### Output Format Requirements - [ ] CHK020 - Is the output format structure explicitly defined with section separators and grouping rules? [Clarity, Spec §FR-006, Contract §responses.success.structure] - [ ] CHK021 - Are requirements specified for section headers and item counts (e.g., "=== Branches (2) ===")? [Clarity, Plan §AI-First Output] - [ ] CHK022 - Is the hierarchical formatting requirement (indentation, grouping) clearly specified? [Clarity, Research §Task 2] - [ ] CHK023 - Are text formatting constraints documented (plain text only, no markdown)? [Clarity, Research §Task 2] ### Empty State Requirements - [ ] CHK024 - Is the empty result message content explicitly specified for issues with no development information? [Clarity, Spec §FR-010, Contract §responses.empty] - [ ] CHK025 - Are requirements clear about distinguishing between "no dev info" vs "API error" states? [Clarity] ### Error Handling Requirements - [ ] CHK026 - Is the error message format requirement specified to include diagnostic context (endpoint, response body)? [Clarity, Spec §FR-007, Plan §AI-First Output] - [ ] CHK027 - Are requirements defined for each error type (invalid key, not found, permission denied, API failure) with distinct messages? [Clarity, Contract §responses.error.cases] --- ## Requirement Consistency ### Cross-Document Alignment - [ ] CHK028 - Do the functional requirements in spec.md align with the data model entities in data-model.md? [Consistency] - [ ] CHK029 - Does the MCP tool contract match the functional requirements (FR-001 through FR-011)? [Consistency, Spec §FR vs Contract] - [ ] CHK030 - Are filter parameter names consistent across spec.md, plan.md, data-model.md, and contract? [Consistency] - [ ] CHK031 - Is the output format description consistent between spec.md (§FR-006), plan.md, research.md, and contract? [Consistency] ### Entity Field Consistency - [ ] CHK032 - Are Branch entity fields consistent between functional requirements (§FR-002) and data model definition? [Consistency] - [ ] CHK033 - Are PullRequest entity fields consistent between functional requirements (§FR-003) and data model definition? [Consistency] - [ ] CHK034 - Are Commit entity fields consistent between functional requirements (§FR-004) and data model definition? [Consistency] ### User Story Coverage - [ ] CHK035 - Do the functional requirements cover all three user stories (view dev work, filter by type, view commits)? [Consistency, Spec §User Scenarios vs §Requirements] - [ ] CHK036 - Are acceptance scenarios for each user story mappable to specific functional requirements? [Traceability] --- ## Acceptance Criteria Quality ### Success Criteria Measurability - [ ] CHK037 - Is "complete development information" (SC-001) defined with specific fields that constitute completeness? [Measurability, Spec §SC-001] - [ ] CHK038 - Is the performance requirement (SC-002: 3 seconds for 50 items) testable with clear pass/fail criteria? [Measurability, Spec §SC-002] - [ ] CHK039 - Is "clearly distinguishes" (SC-003) quantified with specific formatting requirements? [Measurability, Spec §SC-003] - [ ] CHK040 - Is the 100% error handling coverage (SC-005) verifiable with enumerated error cases? [Measurability, Spec §SC-005] ### Acceptance Scenario Completeness - [ ] CHK041 - Do acceptance scenarios for User Story 1 cover both data presence and data absence cases? [Completeness, Spec §US1 Acceptance] - [ ] CHK042 - Do acceptance scenarios for User Story 2 specify expected behavior for all filter combinations? [Completeness, Spec §US2 Acceptance] - [ ] CHK043 - Do acceptance scenarios for User Story 3 address commit grouping by repository? [Completeness, Spec §US3 Acceptance] ### Independent Testability - [ ] CHK044 - Can User Story 1 be tested independently without implementing User Stories 2 or 3? [Independence, Spec §US1 Independent Test] - [ ] CHK045 - Can User Story 2 be tested independently by verifying filter behavior? [Independence, Spec §US2 Independent Test] - [ ] CHK046 - Can User Story 3 be tested independently by verifying commit display? [Independence, Spec §US3 Independent Test] --- ## Scenario Coverage ### Primary Flow Coverage - [ ] CHK047 - Are requirements specified for the primary happy path (issue with branches and PRs exists)? [Coverage, Spec §US1 Acceptance §1-2] - [ ] CHK048 - Are requirements specified for filtering to show only branches? [Coverage, Spec §US2 Acceptance §1] - [ ] CHK049 - Are requirements specified for filtering to show only pull requests? [Coverage, Spec §US2 Acceptance §2] - [ ] CHK050 - Are requirements specified for displaying commits grouped by repository? [Coverage, Spec §US3 Acceptance §2] ### Alternate Flow Coverage - [ ] CHK051 - Are requirements specified for issues with no development information linked? [Coverage, Spec §US1 Acceptance §3] - [ ] CHK052 - Are requirements specified for the default behavior when no filters are specified? [Coverage, Spec §US2 Acceptance §3] - [ ] CHK053 - Are requirements specified for filtering to exclude specific information types? [Coverage, Spec §FR-008] ### Data Variation Coverage - [ ] CHK054 - Are requirements specified for handling branches from multiple repositories? [Coverage, Spec §Edge Cases] - [ ] CHK055 - Are requirements specified for handling PRs in different states (open, merged, closed, declined)? [Coverage, Data Model §PullRequest] - [ ] CHK056 - Are requirements specified for handling commits from multiple VCS providers in one response? [Coverage, Spec §SC-006] --- ## AI/LLM Consumption Requirements ### LLM-Optimized Output - [ ] CHK057 - Are requirements specified for human-readable text format suitable for LLM consumption? [Completeness, Spec §FR-006] - [ ] CHK058 - Is the information organization requirement (by type: branches/PRs/commits) explicitly defined? [Clarity, Spec §FR-006] - [ ] CHK059 - Are requirements specified for self-documenting output with clear labels and context? [Gap, Plan §AI-First Output] - [ ] CHK060 - Are requirements defined for output to be parseable by LLMs without additional formatting instructions? [Gap] ### Tool Discoverability - [ ] CHK061 - Is the tool description requirement written to maximize LLM understanding of tool purpose? [Clarity, Contract §description, Plan §MCP Protocol] - [ ] CHK062 - Are parameter descriptions written for LLM comprehension with examples? [Clarity, Contract §inputSchema] - [ ] CHK063 - Are parameter names self-explanatory for LLM inference (include_branches vs. show_branches)? [Clarity, Research §Task 3] ### Error Message Clarity for LLMs - [ ] CHK064 - Are error message requirements specified to include actionable context for LLM-based troubleshooting? [Clarity, Spec §FR-007, Plan §Error Transparency] - [ ] CHK065 - Are error messages required to include the failing endpoint URL for debugging? [Clarity, Plan §Error Transparency] - [ ] CHK066 - Are requirements specified for error messages to suggest potential causes or remediation? [Gap, Contract §responses.empty] --- ## Technical Implementation Requirements ### MCP Protocol Compliance - [ ] CHK067 - Is the requirement to use MCP typed handlers explicitly stated? [Completeness, Plan §Type Safety] - [ ] CHK068 - Is the requirement for input struct validation (validate:"required" tags) documented? [Completeness, Plan §Type Safety, Data Model] - [ ] CHK069 - Is the requirement to return results via `mcp.NewToolResultText()` specified? [Gap, Plan] - [ ] CHK070 - Is the STDIO mode requirement (vs HTTP mode) documented? [Completeness, Plan §MCP Protocol Compliance] ### Client Connection Requirements - [ ] CHK071 - Is the requirement to reuse singleton Jira client explicitly stated? [Completeness, Spec §FR-009, Plan §Resource Efficiency] - [ ] CHK072 - Is the requirement to avoid per-request client creation documented? [Completeness, Plan §Resource Efficiency] - [ ] CHK073 - Are requirements specified for using `services.JiraClient()` for client access? [Completeness, Plan §Resource Efficiency] ### Code Organization Requirements - [ ] CHK074 - Is the requirement to implement in `tools/jira_development.go` explicitly stated? [Completeness, Plan §Project Structure] - [ ] CHK075 - Is the requirement to register tool in `main.go` documented? [Completeness, Plan §Project Structure, Tasks §T008] - [ ] CHK076 - Is the decision NOT to create a util formatting function documented with rationale? [Clarity, Plan §Simplicity, Research §Task 2] --- ## Dependencies & Assumptions ### External Dependencies - [ ] CHK077 - Is the dependency on go-atlassian v1.6.1 library explicitly documented? [Completeness, Plan §Technical Context] - [ ] CHK078 - Is the dependency on mcp-go v0.32.0 library explicitly documented? [Completeness, Plan §Technical Context] - [ ] CHK079 - Is the assumption about Jira VCS integration configuration documented? [Completeness, Spec §Assumptions] - [ ] CHK080 - Is the assumption about user permissions to view development information documented? [Completeness, Spec §Assumptions] ### API Endpoint Assumptions - [ ] CHK081 - Is the assumption about using an undocumented API clearly stated? [Completeness, Research §Task 1, Data Model §Notes] - [ ] CHK082 - Is the risk of API instability/changes documented? [Gap, Research §Task 1 §Warnings] - [ ] CHK083 - Are the prerequisites (VCS integration enabled in Jira) documented as assumptions? [Completeness, Spec §Assumptions] ### Data Assumptions - [ ] CHK084 - Is the assumption about data retrieval source (Jira stored data vs. direct Git API) documented? [Completeness, Spec §Assumptions] - [ ] CHK085 - Is the assumption about URL availability from Jira API documented? [Completeness, Spec §Assumptions, Data Model §Constraints] - [ ] CHK086 - Is the default filter behavior (all types included) explicitly documented as an assumption? [Completeness, Spec §Assumptions] --- ## Ambiguities & Conflicts ### Potential Ambiguities - [ ] CHK087 - Is "human-readable formatted text" sufficiently defined to be implemented consistently? [Ambiguity, Spec §FR-006] - [ ] CHK088 - Is "appropriate grouping and labels" (SC-003) specific enough for objective verification? [Ambiguity, Spec §SC-003] - [ ] CHK089 - Is "gracefully handle errors" (FR-007) defined with specific error handling behaviors? [Ambiguity, Spec §FR-007] - [ ] CHK090 - Is "last commit" on a branch clearly defined (most recent by timestamp vs. HEAD commit)? [Ambiguity, Data Model §Branch] ### Requirement Gaps - [ ] CHK091 - Are requirements specified for the order of sections in output (branches first vs. commits first)? [Gap] - [ ] CHK092 - Are requirements specified for sorting within sections (branches by name, commits by date)? [Gap] - [ ] CHK093 - Are requirements specified for handling very long commit messages (truncation, wrapping)? [Gap] - [ ] CHK094 - Are requirements specified for displaying timestamp formats (ISO 8601, human-readable, relative)? [Gap] ### Cross-Reference Validation - [ ] CHK095 - Do all functional requirements (FR-001 through FR-011) map to implementation tasks? [Traceability, Spec §FR vs Tasks] - [ ] CHK096 - Do all success criteria (SC-001 through SC-006) have corresponding acceptance scenarios? [Traceability, Spec §SC vs §Acceptance Scenarios] - [ ] CHK097 - Do all edge cases listed in spec.md have corresponding requirements or are marked as out-of-scope? [Coverage, Spec §Edge Cases] --- ## Non-Functional Requirements ### Performance Requirements - [ ] CHK098 - Is the performance requirement (3 seconds for 50 items) supported by implementation architecture decisions? [Consistency, Spec §SC-002 vs Plan §Technical Context] - [ ] CHK099 - Are performance requirements specified for larger data sets (>50 items) or is there an explicit limit? [Gap, Spec §Edge Cases] - [ ] CHK100 - Are requirements specified for performance degradation behavior when API is slow? [Gap] ### Resource Efficiency Requirements - [ ] CHK101 - Is the singleton pattern requirement traceable to specific resource efficiency goals? [Traceability, Plan §Resource Efficiency] - [ ] CHK102 - Are requirements specified for API call minimization (single call per tool invocation)? [Completeness, Plan §Resource Efficiency] - [ ] CHK103 - Are requirements specified for memory usage when handling large result sets? [Gap] ### Maintainability Requirements - [ ] CHK104 - Is the requirement for inline formatting (avoiding util functions) documented with justification? [Completeness, Plan §Simplicity, Research §Task 2] - [ ] CHK105 - Are requirements specified for code documentation (comments, godoc)? [Gap, Tasks §T018] - [ ] CHK106 - Is the requirement for following existing tool patterns (typed handlers) explicitly stated? [Completeness, Plan §Constitution Check] --- ## Testing Requirements ### Test Coverage Requirements - [ ] CHK107 - Are integration test requirements specified for tool registration validation? [Completeness, Plan §Testing Gates, Tasks §T009] - [ ] CHK108 - Are integration test requirements specified for happy path scenarios (issue with dev info)? [Completeness, Tasks §T009] - [ ] CHK109 - Are integration test requirements specified for empty state scenarios (issue without dev info)? [Completeness, Tasks §T009] - [ ] CHK110 - Are integration test requirements specified for error scenarios (invalid key, 404)? [Completeness, Tasks §T009] ### Test Execution Requirements - [ ] CHK111 - Are requirements specified for test execution via `go test ./...`? [Completeness, Plan §Testing] - [ ] CHK112 - Are prerequisites for integration tests (live Jira instance, VCS integration) documented? [Completeness, Contract §testing.integration.prerequisites] - [ ] CHK113 - Are requirements specified for independent testability of each user story? [Completeness, Spec §User Stories §Independent Test] --- ## Summary **Total Items**: 113 checklist items **Coverage Breakdown**: - Requirement Completeness: 16 items (CHK001-CHK016) - Requirement Clarity: 11 items (CHK017-CHK027) - Requirement Consistency: 9 items (CHK028-CHK036) - Acceptance Criteria Quality: 10 items (CHK037-CHK046) - Scenario Coverage: 10 items (CHK047-CHK056) - AI/LLM Consumption: 10 items (CHK057-CHK066) - Technical Implementation: 10 items (CHK067-CHK076) - Dependencies & Assumptions: 10 items (CHK077-CHK086) - Ambiguities & Conflicts: 11 items (CHK087-CHK097) - Non-Functional Requirements: 9 items (CHK098-CHK106) - Testing Requirements: 7 items (CHK107-CHK113) **Traceability**: 87% of items include spec/plan/data-model references or gap markers **Focus Areas Validated**: - ✅ MCP tool contract completeness and clarity - ✅ API integration requirements (endpoint, parameters, data model) - ✅ AI-first output requirements (LLM-readable formatting, tool descriptions) - ✅ Happy path scenario coverage (primary flows for all 3 user stories) - ✅ Technical requirements (MCP protocol, singleton client, code organization) - ✅ Acceptance criteria measurability **Next Steps**: 1. Review checklist items marked with [Gap] - these indicate missing requirements 2. Resolve items marked with [Ambiguity] or [Conflict] before implementation 3. Verify all [Completeness] items reference existing requirement documentation 4. Use this checklist to validate spec.md/plan.md updates before beginning implementation ``` -------------------------------------------------------------------------------- /.specify/scripts/bash/update-agent-context.sh: -------------------------------------------------------------------------------- ```bash #!/usr/bin/env bash # Update agent context files with information from plan.md # # This script maintains AI agent context files by parsing feature specifications # and updating agent-specific configuration files with project information. # # MAIN FUNCTIONS: # 1. Environment Validation # - Verifies git repository structure and branch information # - Checks for required plan.md files and templates # - Validates file permissions and accessibility # # 2. Plan Data Extraction # - Parses plan.md files to extract project metadata # - Identifies language/version, frameworks, databases, and project types # - Handles missing or incomplete specification data gracefully # # 3. Agent File Management # - Creates new agent context files from templates when needed # - Updates existing agent files with new project information # - Preserves manual additions and custom configurations # - Supports multiple AI agent formats and directory structures # # 4. Content Generation # - Generates language-specific build/test commands # - Creates appropriate project directory structures # - Updates technology stacks and recent changes sections # - Maintains consistent formatting and timestamps # # 5. Multi-Agent Support # - Handles agent-specific file paths and naming conventions # - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, or Amazon Q Developer CLI # - Can update single agents or all existing agent files # - Creates default Claude file if no agent files exist # # Usage: ./update-agent-context.sh [agent_type] # Agent types: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|q # Leave empty to update all existing agent files set -e # Enable strict error handling set -u set -o pipefail #============================================================================== # Configuration and Global Variables #============================================================================== # Get script directory and load common functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/common.sh" # Get all paths and variables from common functions eval $(get_feature_paths) NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code AGENT_TYPE="${1:-}" # Agent-specific file paths CLAUDE_FILE="$REPO_ROOT/CLAUDE.md" GEMINI_FILE="$REPO_ROOT/GEMINI.md" COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md" CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc" QWEN_FILE="$REPO_ROOT/QWEN.md" AGENTS_FILE="$REPO_ROOT/AGENTS.md" WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md" KILOCODE_FILE="$REPO_ROOT/.kilocode/rules/specify-rules.md" AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md" ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md" Q_FILE="$REPO_ROOT/.amazonq/rules/specify-rules.md" # Template file TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md" # Global variables for parsed plan data NEW_LANG="" NEW_FRAMEWORK="" NEW_DB="" NEW_PROJECT_TYPE="" #============================================================================== # Utility Functions #============================================================================== log_info() { echo "INFO: $1" } log_success() { echo "✓ $1" } log_error() { echo "ERROR: $1" >&2 } log_warning() { echo "WARNING: $1" >&2 } # Cleanup function for temporary files cleanup() { local exit_code=$? rm -f /tmp/agent_update_*_$$ rm -f /tmp/manual_additions_$$ exit $exit_code } # Set up cleanup trap trap cleanup EXIT INT TERM #============================================================================== # Validation Functions #============================================================================== validate_environment() { # Check if we have a current branch/feature (git or non-git) if [[ -z "$CURRENT_BRANCH" ]]; then log_error "Unable to determine current feature" if [[ "$HAS_GIT" == "true" ]]; then log_info "Make sure you're on a feature branch" else log_info "Set SPECIFY_FEATURE environment variable or create a feature first" fi exit 1 fi # Check if plan.md exists if [[ ! -f "$NEW_PLAN" ]]; then log_error "No plan.md found at $NEW_PLAN" log_info "Make sure you're working on a feature with a corresponding spec directory" if [[ "$HAS_GIT" != "true" ]]; then log_info "Use: export SPECIFY_FEATURE=your-feature-name or create a new feature first" fi exit 1 fi # Check if template exists (needed for new files) if [[ ! -f "$TEMPLATE_FILE" ]]; then log_warning "Template file not found at $TEMPLATE_FILE" log_warning "Creating new agent files will fail" fi } #============================================================================== # Plan Parsing Functions #============================================================================== extract_plan_field() { local field_pattern="$1" local plan_file="$2" grep "^\*\*${field_pattern}\*\*: " "$plan_file" 2>/dev/null | \ head -1 | \ sed "s|^\*\*${field_pattern}\*\*: ||" | \ sed 's/^[ \t]*//;s/[ \t]*$//' | \ grep -v "NEEDS CLARIFICATION" | \ grep -v "^N/A$" || echo "" } parse_plan_data() { local plan_file="$1" if [[ ! -f "$plan_file" ]]; then log_error "Plan file not found: $plan_file" return 1 fi if [[ ! -r "$plan_file" ]]; then log_error "Plan file is not readable: $plan_file" return 1 fi log_info "Parsing plan data from $plan_file" NEW_LANG=$(extract_plan_field "Language/Version" "$plan_file") NEW_FRAMEWORK=$(extract_plan_field "Primary Dependencies" "$plan_file") NEW_DB=$(extract_plan_field "Storage" "$plan_file") NEW_PROJECT_TYPE=$(extract_plan_field "Project Type" "$plan_file") # Log what we found if [[ -n "$NEW_LANG" ]]; then log_info "Found language: $NEW_LANG" else log_warning "No language information found in plan" fi if [[ -n "$NEW_FRAMEWORK" ]]; then log_info "Found framework: $NEW_FRAMEWORK" fi if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then log_info "Found database: $NEW_DB" fi if [[ -n "$NEW_PROJECT_TYPE" ]]; then log_info "Found project type: $NEW_PROJECT_TYPE" fi } format_technology_stack() { local lang="$1" local framework="$2" local parts=() # Add non-empty parts [[ -n "$lang" && "$lang" != "NEEDS CLARIFICATION" ]] && parts+=("$lang") [[ -n "$framework" && "$framework" != "NEEDS CLARIFICATION" && "$framework" != "N/A" ]] && parts+=("$framework") # Join with proper formatting if [[ ${#parts[@]} -eq 0 ]]; then echo "" elif [[ ${#parts[@]} -eq 1 ]]; then echo "${parts[0]}" else # Join multiple parts with " + " local result="${parts[0]}" for ((i=1; i<${#parts[@]}; i++)); do result="$result + ${parts[i]}" done echo "$result" fi } #============================================================================== # Template and Content Generation Functions #============================================================================== get_project_structure() { local project_type="$1" if [[ "$project_type" == *"web"* ]]; then echo "backend/\\nfrontend/\\ntests/" else echo "src/\\ntests/" fi } get_commands_for_language() { local lang="$1" case "$lang" in *"Python"*) echo "cd src && pytest && ruff check ." ;; *"Rust"*) echo "cargo test && cargo clippy" ;; *"JavaScript"*|*"TypeScript"*) echo "npm test && npm run lint" ;; *) echo "# Add commands for $lang" ;; esac } get_language_conventions() { local lang="$1" echo "$lang: Follow standard conventions" } create_new_agent_file() { local target_file="$1" local temp_file="$2" local project_name="$3" local current_date="$4" if [[ ! -f "$TEMPLATE_FILE" ]]; then log_error "Template not found at $TEMPLATE_FILE" return 1 fi if [[ ! -r "$TEMPLATE_FILE" ]]; then log_error "Template file is not readable: $TEMPLATE_FILE" return 1 fi log_info "Creating new agent context file from template..." if ! cp "$TEMPLATE_FILE" "$temp_file"; then log_error "Failed to copy template file" return 1 fi # Replace template placeholders local project_structure project_structure=$(get_project_structure "$NEW_PROJECT_TYPE") local commands commands=$(get_commands_for_language "$NEW_LANG") local language_conventions language_conventions=$(get_language_conventions "$NEW_LANG") # Perform substitutions with error checking using safer approach # Escape special characters for sed by using a different delimiter or escaping local escaped_lang=$(printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|]/\\&/g') local escaped_framework=$(printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|]/\\&/g') local escaped_branch=$(printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|]/\\&/g') # Build technology stack and recent change strings conditionally local tech_stack if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then tech_stack="- $escaped_lang + $escaped_framework ($escaped_branch)" elif [[ -n "$escaped_lang" ]]; then tech_stack="- $escaped_lang ($escaped_branch)" elif [[ -n "$escaped_framework" ]]; then tech_stack="- $escaped_framework ($escaped_branch)" else tech_stack="- ($escaped_branch)" fi local recent_change if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then recent_change="- $escaped_branch: Added $escaped_lang + $escaped_framework" elif [[ -n "$escaped_lang" ]]; then recent_change="- $escaped_branch: Added $escaped_lang" elif [[ -n "$escaped_framework" ]]; then recent_change="- $escaped_branch: Added $escaped_framework" else recent_change="- $escaped_branch: Added" fi local substitutions=( "s|\[PROJECT NAME\]|$project_name|" "s|\[DATE\]|$current_date|" "s|\[EXTRACTED FROM ALL PLAN.MD FILES\]|$tech_stack|" "s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g" "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|" "s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|" "s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|$recent_change|" ) for substitution in "${substitutions[@]}"; do if ! sed -i.bak -e "$substitution" "$temp_file"; then log_error "Failed to perform substitution: $substitution" rm -f "$temp_file" "$temp_file.bak" return 1 fi done # Convert \n sequences to actual newlines newline=$(printf '\n') sed -i.bak2 "s/\\\\n/${newline}/g" "$temp_file" # Clean up backup files rm -f "$temp_file.bak" "$temp_file.bak2" return 0 } update_existing_agent_file() { local target_file="$1" local current_date="$2" log_info "Updating existing agent context file..." # Use a single temporary file for atomic update local temp_file temp_file=$(mktemp) || { log_error "Failed to create temporary file" return 1 } # Process the file in one pass local tech_stack=$(format_technology_stack "$NEW_LANG" "$NEW_FRAMEWORK") local new_tech_entries=() local new_change_entry="" # Prepare new technology entries if [[ -n "$tech_stack" ]] && ! grep -q "$tech_stack" "$target_file"; then new_tech_entries+=("- $tech_stack ($CURRENT_BRANCH)") fi if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]] && ! grep -q "$NEW_DB" "$target_file"; then new_tech_entries+=("- $NEW_DB ($CURRENT_BRANCH)") fi # Prepare new change entry if [[ -n "$tech_stack" ]]; then new_change_entry="- $CURRENT_BRANCH: Added $tech_stack" elif [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]]; then new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB" fi # Process file line by line local in_tech_section=false local in_changes_section=false local tech_entries_added=false local changes_entries_added=false local existing_changes_count=0 while IFS= read -r line || [[ -n "$line" ]]; do # Handle Active Technologies section if [[ "$line" == "## Active Technologies" ]]; then echo "$line" >> "$temp_file" in_tech_section=true continue elif [[ $in_tech_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then # Add new tech entries before closing the section if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file" tech_entries_added=true fi echo "$line" >> "$temp_file" in_tech_section=false continue elif [[ $in_tech_section == true ]] && [[ -z "$line" ]]; then # Add new tech entries before empty line in tech section if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file" tech_entries_added=true fi echo "$line" >> "$temp_file" continue fi # Handle Recent Changes section if [[ "$line" == "## Recent Changes" ]]; then echo "$line" >> "$temp_file" # Add new change entry right after the heading if [[ -n "$new_change_entry" ]]; then echo "$new_change_entry" >> "$temp_file" fi in_changes_section=true changes_entries_added=true continue elif [[ $in_changes_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then echo "$line" >> "$temp_file" in_changes_section=false continue elif [[ $in_changes_section == true ]] && [[ "$line" == "- "* ]]; then # Keep only first 2 existing changes if [[ $existing_changes_count -lt 2 ]]; then echo "$line" >> "$temp_file" ((existing_changes_count++)) fi continue fi # Update timestamp if [[ "$line" =~ \*\*Last\ updated\*\*:.*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then echo "$line" | sed "s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/$current_date/" >> "$temp_file" else echo "$line" >> "$temp_file" fi done < "$target_file" # Post-loop check: if we're still in the Active Technologies section and haven't added new entries if [[ $in_tech_section == true ]] && [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file" fi # Move temp file to target atomically if ! mv "$temp_file" "$target_file"; then log_error "Failed to update target file" rm -f "$temp_file" return 1 fi return 0 } #============================================================================== # Main Agent File Update Function #============================================================================== update_agent_file() { local target_file="$1" local agent_name="$2" if [[ -z "$target_file" ]] || [[ -z "$agent_name" ]]; then log_error "update_agent_file requires target_file and agent_name parameters" return 1 fi log_info "Updating $agent_name context file: $target_file" local project_name project_name=$(basename "$REPO_ROOT") local current_date current_date=$(date +%Y-%m-%d) # Create directory if it doesn't exist local target_dir target_dir=$(dirname "$target_file") if [[ ! -d "$target_dir" ]]; then if ! mkdir -p "$target_dir"; then log_error "Failed to create directory: $target_dir" return 1 fi fi if [[ ! -f "$target_file" ]]; then # Create new file from template local temp_file temp_file=$(mktemp) || { log_error "Failed to create temporary file" return 1 } if create_new_agent_file "$target_file" "$temp_file" "$project_name" "$current_date"; then if mv "$temp_file" "$target_file"; then log_success "Created new $agent_name context file" else log_error "Failed to move temporary file to $target_file" rm -f "$temp_file" return 1 fi else log_error "Failed to create new agent file" rm -f "$temp_file" return 1 fi else # Update existing file if [[ ! -r "$target_file" ]]; then log_error "Cannot read existing file: $target_file" return 1 fi if [[ ! -w "$target_file" ]]; then log_error "Cannot write to existing file: $target_file" return 1 fi if update_existing_agent_file "$target_file" "$current_date"; then log_success "Updated existing $agent_name context file" else log_error "Failed to update existing agent file" return 1 fi fi return 0 } #============================================================================== # Agent Selection and Processing #============================================================================== update_specific_agent() { local agent_type="$1" case "$agent_type" in claude) update_agent_file "$CLAUDE_FILE" "Claude Code" ;; gemini) update_agent_file "$GEMINI_FILE" "Gemini CLI" ;; copilot) update_agent_file "$COPILOT_FILE" "GitHub Copilot" ;; cursor) update_agent_file "$CURSOR_FILE" "Cursor IDE" ;; qwen) update_agent_file "$QWEN_FILE" "Qwen Code" ;; opencode) update_agent_file "$AGENTS_FILE" "opencode" ;; codex) update_agent_file "$AGENTS_FILE" "Codex CLI" ;; windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;; kilocode) update_agent_file "$KILOCODE_FILE" "Kilo Code" ;; auggie) update_agent_file "$AUGGIE_FILE" "Auggie CLI" ;; roo) update_agent_file "$ROO_FILE" "Roo Code" ;; q) update_agent_file "$Q_FILE" "Amazon Q Developer CLI" ;; *) log_error "Unknown agent type '$agent_type'" log_error "Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo|q" exit 1 ;; esac } update_all_existing_agents() { local found_agent=false # Check each possible agent file and update if it exists if [[ -f "$CLAUDE_FILE" ]]; then update_agent_file "$CLAUDE_FILE" "Claude Code" found_agent=true fi if [[ -f "$GEMINI_FILE" ]]; then update_agent_file "$GEMINI_FILE" "Gemini CLI" found_agent=true fi if [[ -f "$COPILOT_FILE" ]]; then update_agent_file "$COPILOT_FILE" "GitHub Copilot" found_agent=true fi if [[ -f "$CURSOR_FILE" ]]; then update_agent_file "$CURSOR_FILE" "Cursor IDE" found_agent=true fi if [[ -f "$QWEN_FILE" ]]; then update_agent_file "$QWEN_FILE" "Qwen Code" found_agent=true fi if [[ -f "$AGENTS_FILE" ]]; then update_agent_file "$AGENTS_FILE" "Codex/opencode" found_agent=true fi if [[ -f "$WINDSURF_FILE" ]]; then update_agent_file "$WINDSURF_FILE" "Windsurf" found_agent=true fi if [[ -f "$KILOCODE_FILE" ]]; then update_agent_file "$KILOCODE_FILE" "Kilo Code" found_agent=true fi if [[ -f "$AUGGIE_FILE" ]]; then update_agent_file "$AUGGIE_FILE" "Auggie CLI" found_agent=true fi if [[ -f "$ROO_FILE" ]]; then update_agent_file "$ROO_FILE" "Roo Code" found_agent=true fi if [[ -f "$Q_FILE" ]]; then update_agent_file "$Q_FILE" "Amazon Q Developer CLI" found_agent=true fi # If no agent files exist, create a default Claude file if [[ "$found_agent" == false ]]; then log_info "No existing agent files found, creating default Claude file..." update_agent_file "$CLAUDE_FILE" "Claude Code" fi } print_summary() { echo log_info "Summary of changes:" if [[ -n "$NEW_LANG" ]]; then echo " - Added language: $NEW_LANG" fi if [[ -n "$NEW_FRAMEWORK" ]]; then echo " - Added framework: $NEW_FRAMEWORK" fi if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then echo " - Added database: $NEW_DB" fi echo log_info "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|q]" } #============================================================================== # Main Execution #============================================================================== main() { # Validate environment before proceeding validate_environment log_info "=== Updating agent context files for feature $CURRENT_BRANCH ===" # Parse the plan file to extract project information if ! parse_plan_data "$NEW_PLAN"; then log_error "Failed to parse plan data" exit 1 fi # Process based on agent type argument local success=true if [[ -z "$AGENT_TYPE" ]]; then # No specific agent provided - update all existing agent files log_info "No agent specified, updating all existing agent files..." if ! update_all_existing_agents; then success=false fi else # Specific agent provided - update only that agent log_info "Updating specific agent: $AGENT_TYPE" if ! update_specific_agent "$AGENT_TYPE"; then success=false fi fi # Print summary print_summary if [[ "$success" == true ]]; then log_success "Agent context update completed successfully" exit 0 else log_error "Agent context update completed with errors" exit 1 fi } # Execute main function if script is run directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi ```