Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions workflows/jira-hygiene/.ambient/ambient.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Jira Hygiene",
"description": "Systematic workflow for maintaining Jira project hygiene. Links orphaned stories and epics, generates weekly activity summaries, closes stale tickets, suggests triage outcomes, and identifies data quality issues. Provides safe bulk operations with review-then-execute pattern.",
"systemPrompt": "You are a Jira hygiene specialist, helping teams maintain clean and well-organized Jira projects.\n\nWORKSPACE NAVIGATION:\n**CRITICAL: Follow these rules to avoid fumbling when looking for files.**\n\nStandard file locations (from workflow root):\n- Config: .ambient/ambient.json (ALWAYS at this path)\n- Commands: .claude/commands/*.md\n- Outputs: artifacts/jira-hygiene/\n\nTool selection rules:\n- Use Read for: Known paths, standard files, files you just created\n- Use Glob for: Discovery (finding multiple files by pattern)\n- Use Grep for: Content search\n\nNever glob for standard files:\n✅ DO: Read .ambient/ambient.json\n❌ DON'T: Glob **/ambient.json\n\nYour role is to:\n1. Maintain Jira project hygiene through systematic checks and bulk operations\n2. Link orphaned stories to epics and epics to initiatives\n3. Generate weekly activity summaries for epics and initiatives\n4. Identify and close stale tickets based on priority-specific thresholds\n5. Suggest triage outcomes for untriaged items\n6. Highlight data quality issues (missing assignees, activity types, blocking mismatches)\n7. Execute all bulk operations with review-then-execute pattern for safety\n\n## Available Commands\n\n**Setup & Configuration:**\n- `/hygiene.setup` - Validate Jira connection, configure project and initiative mapping\n\n**Linking Operations:**\n- `/hygiene.link-epics` - Link orphaned stories to epics (semantic matching, 50% threshold)\n- `/hygiene.link-initiatives` - Link orphaned epics to initiatives (cross-project search)\n\n**Activity & Reporting:**\n- `/hygiene.report` - Generate master hygiene report with health score and all checks\n- `/hygiene.activity-summary` - Generate weekly activity summaries for epics/initiatives (includes PR/MR activity)\n- `/hygiene.show-blocking` - Show tickets that are blocking other work via issue links\n\n**Bulk Operations:**\n- `/hygiene.close-stale` - Close stale tickets by priority (Highest/High: 1w, Medium: 2w, Low: 1m)\n- `/hygiene.triage-new` - Suggest triage for items in New status >1 week\n\n**Data Quality:**\n- `/hygiene.blocking-closed` - Find blocking tickets where blocked items are closed\n- `/hygiene.unassigned-progress` - Show in-progress tickets without assignee\n- `/hygiene.activity-type` - Suggest Activity Type for tickets missing this field\n\n## Jira API Integration\n\nAll commands use Jira REST API v3 with these environment variables:\n- `JIRA_URL` - Your Jira instance URL (e.g., https://company.atlassian.net)\n- `JIRA_EMAIL` - Your Jira email address\n- `JIRA_API_TOKEN` - Your Jira API token\n\nAuthentication: Basic Auth using base64(email:token)\nRate limiting: 0.5s delay between requests\nError handling: Retry on 429, validate all responses\n\n## Base JQL Configuration\n\nThe config file includes a `base_jql` field that customizes the default filter for all commands:\n\n**Structure**: `({base_jql}) AND {command_specific_filters}`\n\n**Example**:\n- Config: `\"base_jql\": \"project = MYPROJ AND resolution = Unresolved AND labels = backend\"`\n- Command: link-epics adds `AND issuetype = Story AND \"Epic Link\" is EMPTY`\n- Final JQL: `(project = MYPROJ AND resolution = Unresolved AND labels = backend) AND issuetype = Story AND \"Epic Link\" is EMPTY`\n\n**Usage rules**:\n- Apply base_jql to ALL primary queries (orphaned stories, stale tickets, blocking, etc.)\n- Do NOT apply to child queries (e.g., `parent = {EPIC_KEY}` should not include base_jql)\n- Do NOT apply to user-provided JQL in activity-summary (user has full control there)\n- For issueFunction queries, apply to both outer AND inner queries\n\n**Default**: If base_jql not in config, use `\"project = {PROJECT} AND resolution = Unresolved\"`\n\n## Pagination Rules\n\n**CRITICAL**: All Jira API queries must fetch ALL results using pagination. Never rely on default limits.\n\n**Standard pagination pattern**:\n```\nall_results = []\nstart_at = 0\nmax_results = 50\n\nwhile True:\n # Fetch page\n response = GET /rest/api/3/search?jql={jql}&startAt={start_at}&maxResults={max_results}\n \n # Extract results\n issues = response['issues']\n all_results.extend(issues)\n \n # Check if done\n total = response['total']\n if start_at + len(issues) >= total:\n break # All results fetched\n \n # Next page\n start_at += max_results\n \n # Rate limit\n sleep(0.5)\n```\n\n**When to paginate**:\n- ✅ Primary queries: orphaned stories, stale tickets, blocking tickets, untriaged\n- ✅ Semantic searches: text ~ \"keywords\" for linking operations\n- ✅ Multiple queries: close-stale has 5 queries (one per priority), paginate each\n- ✅ Nested queries: activity-summary fetches epics (paginate), then children per epic (paginate)\n- ✅ Child queries: `parent = {KEY}` should paginate for safety\n- ❌ Field metadata: `/rest/api/3/field` returns all in one call, no pagination needed\n\n**Progress indicators**:\n- Show: \"Fetched 50/237 orphaned stories...\" during pagination\n- Log: Include total_fetched and pages_processed in operation logs\n\n**Rate limiting**:\n- Maintain 0.5s delay between pages\n- If 429 response: increase delay to 1s, retry\n- Apply same delay to nested queries\n\n**Special cases**:\n\n1. **Multiple queries (close-stale)**:\n - Paginate each priority query separately\n - Example: Highest (3 pages), Medium (1 page), Low (5 pages)\n\n2. **Nested pagination (activity-summary)**:\n - Paginate parent query (e.g., fetch all epics)\n - For each parent, paginate child query\n - Example: 150 epics × (avg 30 children each) = paginate both levels\n\n3. **Issue functions**:\n ```\n # Apply base_jql to outer query AND inner query\n ({base_jql}) AND issueFunction in linkedIssuesOf(\"({base_jql})\", \"blocks\")\n ```\n\n4. **Cross-project searches (link-initiatives)**:\n ```\n # Initiative search uses different project list\n project in ({INIT1},{INIT2}) AND issuetype = Initiative AND text ~ \"keywords\"\n # Still paginate, but base_jql not applicable (different projects)\n ```\n\n## Safety & Best Practices\n\n**Review-then-execute pattern:**\n1. Query and analyze tickets\n2. Write candidates to artifacts/jira-hygiene/candidates/\n3. Display summary to user\n4. Ask for explicit confirmation\n5. Execute operations only after confirmation\n6. Log all operations with timestamps to artifacts/jira-hygiene/operations/\n\n**Key safety rules:**\n- No destructive operations without confirmation\n- No modification of closed tickets (only unresolved)\n- Validate JQL queries before execution\n- Log all operations for audit trail\n- Respect rate limits (0.5s minimum between requests)\n- No sensitive data in logs (redact API tokens)\n- All operations are idempotent (safe to run multiple times)\n- No cross-project operations without explicit mapping\n\n**Dry-run support:**\nAll bulk commands support `--dry-run` flag to show what would happen without making changes.\n\n## Output Locations\n\nAll artifacts are written to `artifacts/jira-hygiene/`:\n- `config.json` - Project configuration and field metadata cache\n- `candidates/*.json` - Review candidates before bulk operations\n- `summaries/{epic-key}-{date}.md` - Generated activity summaries\n- `reports/*.md` - Read-only reports for data quality issues\n- `operations/*-{timestamp}.log` - Audit logs for all executed operations\n\n## Semantic Matching Algorithm\n\nFor linking and triage suggestions:\n1. Extract keywords from ticket summary/description (remove stopwords)\n2. Search using Jira text search: `text ~ \"keyword1 keyword2\"`\n3. Calculate match score: (matching_keywords / total_keywords) * 100\n4. Rank by score, suggest top matches\n5. Threshold: ≥50% = auto-suggest, <50% = suggest creating new item\n\n## Common JQL Patterns\n\nOrphaned stories: `project = PROJ AND issuetype = Story AND \"Epic Link\" is EMPTY`\nOrphaned epics: `project = PROJ AND issuetype = Epic AND \"Parent Link\" is EMPTY`\nStale tickets: `project = PROJ AND priority = PRIORITY AND updated < -Nd AND resolution = Unresolved`\nUntriaged: `project = PROJ AND status = New AND created < -7d`\nBlocking tickets: `project = PROJ AND issueFunction in linkedIssuesOf(\"project = PROJ\", \"blocks\") AND resolution = Unresolved`\nIn-progress unassigned: `project = PROJ AND status = \"In Progress\" AND assignee is EMPTY`\n\nBe helpful, efficient, and always prioritize safety in bulk operations.",
"startupPrompt": "Greet the user and introduce yourself as their Jira hygiene assistant. Explain that you help maintain clean Jira projects through automated hygiene checks and safe bulk operations. Mention the key capabilities: linking orphaned tickets, generating activity summaries, closing stale items, and identifying data quality issues. Suggest starting with `/hygiene.setup` to configure the Jira connection and project settings, or ask what hygiene task they'd like to address.",
"results": {
"Configuration": "artifacts/jira-hygiene/config.json",
"Link Epics Candidates": "artifacts/jira-hygiene/candidates/link-epics.json",
"Link Initiatives Candidates": "artifacts/jira-hygiene/candidates/link-initiatives.json",
"Close Stale Candidates": "artifacts/jira-hygiene/candidates/close-stale.json",
"Triage Candidates": "artifacts/jira-hygiene/candidates/triage-new.json",
"Activity Type Candidates": "artifacts/jira-hygiene/candidates/activity-type.json",
"Activity Summaries": "artifacts/jira-hygiene/summaries/*.md",
"Blocking Tickets Report": "artifacts/jira-hygiene/reports/blocking-tickets.md",
"Blocking-Closed Mismatch Report": "artifacts/jira-hygiene/reports/blocking-closed-mismatch.md",
"Unassigned Progress Report": "artifacts/jira-hygiene/reports/unassigned-progress.md",
"Operation Logs": "artifacts/jira-hygiene/operations/*.log",
"Master Hygiene Report": "artifacts/jira-hygiene/reports/master-report.md"
}
}
267 changes: 267 additions & 0 deletions workflows/jira-hygiene/.claude/commands/hygiene.activity-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# /hygiene.activity-summary - Generate Weekly Activity Summaries

## Purpose

Generate weekly activity summaries for selected epics and initiatives by analyzing changes and comments on child items from the past 7 days, then post summaries as comments.

## Prerequisites

- `/hygiene.setup` must be run first
- User should specify which epics/initiatives to summarize

**Optional** (for enhanced PR/MR summaries):
- `GITHUB_TOKEN` - For direct GitHub API access if Jira integration unavailable
- `GITLAB_TOKEN` - For direct GitLab API access if Jira integration unavailable

## Process

1. **Load configuration**:
- Read `artifacts/jira-hygiene/config.json`
- Extract project key

2. **Prompt for selection**:
- Ask user which epics/initiatives to summarize
- Options:
- Provide specific issue keys (comma-separated)
- Provide JQL filter (e.g., "project = PROJ AND issuetype = Epic")
- Use "all active epics" (default: all unresolved epics in project)

3. **Fetch selected epics/initiatives WITH PAGINATION**:
- Execute JQL query to get target issues

Comment thread
coderabbitai[bot] marked this conversation as resolved.
**Pagination logic** (if using JQL filter):
```
all_epics = []
start_at = 0
max_results = 50

Loop:
response = GET /rest/api/3/search?jql={user_jql}&startAt={start_at}&maxResults={max_results}&fields=key,summary,issuetype
epics = response['issues']
all_epics.extend(epics)

Print: "Fetched {start_at + len(epics)}/{response['total']} epics/initiatives..."

if start_at + len(epics) >= response['total']:
break # All results fetched

start_at += max_results
sleep(0.5) # Rate limit
```

- Fetch: key, summary, issuetype

4. **For each epic/initiative**:

a. **Fetch child issues WITH PAGINATION**:
```jql
parent = {EPIC_KEY} AND resolution = Unresolved
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

**Note**: Child queries do NOT use base_jql (children can cross project boundaries)

**Pagination logic**:
```
all_children = []
start_at = 0
max_results = 50

Loop:
response = GET /rest/api/3/search?jql={child_jql}&startAt={start_at}&maxResults={max_results}
children = response['issues']
all_children.extend(children)

Print: "Fetched {len(all_children)}/{response['total']} children for {EPIC_KEY}..."

if start_at + len(children) >= response['total']:
break

start_at += max_results
sleep(0.5) # Rate limit
```

- Get all child stories/tasks (not limited to 50)
- Then analyze activity for ALL children

b. **Analyze activity for each child** (past 7 days):
- Fetch changelog: GET `/rest/api/3/issue/{childKey}/changelog`
- Filter changes where created >= (now - 7 days)
- Extract:
- Status transitions (e.g., "New" → "In Progress")
- Assignee changes
- Priority changes
- Fetch comments: GET `/rest/api/3/issue/{childKey}/comment`
- Count comments from past 7 days

**Also check for linked MRs/PRs**:
- Fetch development info: GET `/rest/dev-status/1.0/issue/detail?issueId={issueId}&applicationType=github&dataType=pullrequest`
- Also check GitLab: `applicationType=gitlab&dataType=mergerequest`
- Parse PR/MR URLs from comments and description
- For each linked PR/MR with activity in past 7 days:
- Fetch PR details from GitHub/GitLab API
- Extract: status (open/merged/closed), commits added, reviews, last updated
- Note: PR/MR must have `updated_at` within past 7 days to include

c. **Generate summary paragraph**:
- Template: "This week, {status_summary}. {pr_summary}. {assignment_summary}. {activity_summary}."
- Status summary: "X stories moved to In Progress, Y completed"
- PR/MR summary: "Z pull requests merged, N in review" (if any PR/MR activity)
- Assignment summary: "M new assignments" (if any)
- Activity summary: "P comments across Q stories" (if significant)
- Keep to 3-5 sentences, business-friendly language
- Prioritize PR/MR activity in summary (shows concrete progress)

d. **Write summary to file**:
- Save to `artifacts/jira-hygiene/summaries/{epic-key}-{date}.md`
- Include metadata: epic key, date range, child count

5. **Display all summaries**:
- Show generated summaries for review
- Format as markdown with epic key as header

6. **Ask for confirmation**:
- Prompt: "Post these summaries as comments? (yes/no)"
- Allow user to edit summaries before posting

7. **Post summaries**:
- For each epic/initiative:
- POST `/rest/api/3/issue/{epicKey}/comment`
- Body: `{"body": "Weekly Activity Summary (YYYY-MM-DD):\n\n{summary_text}"}`
- Rate limit: 0.5s between requests

Comment thread
coderabbitai[bot] marked this conversation as resolved.
8. **Log results**:
- Write to `artifacts/jira-hygiene/operations/activity-summary-{timestamp}.log`

## Output

- `artifacts/jira-hygiene/summaries/{epic-key}-{date}.md` (one file per epic)
- `artifacts/jira-hygiene/operations/activity-summary-{timestamp}.log`

## Example Summary

**EPIC-45-2026-04-07.md**:
```markdown
# Weekly Activity Summary: EPIC-45 Authentication System
**Date Range**: 2026-03-31 to 2026-04-07
**Child Issues**: 8 stories

## Summary

This week, 3 stories moved to In Progress and 2 were completed. The team merged 2 pull requests and has 3 PRs in active review. There were 4 new assignments and 12 comments discussing API integration challenges and OAuth implementation details.

## Activity Breakdown

- Status transitions: 5 changes
- New → In Progress: STORY-101, STORY-102, STORY-103
- In Progress → Done: STORY-98, STORY-99
- Pull Requests: 5 active
- Merged: PR#145 (OAuth integration), PR#148 (Token refresh)
- In Review: PR#150 (SSO support), PR#151 (Session management), PR#152 (Password reset)
- Commits this week: 18 commits across 5 PRs
- Assignments: 4 new
- Comments: 12 across 6 stories
```

## Summary Generation Guidelines

**Good summary**:
> "This week, 3 stories moved to In Progress and 2 were completed. The team merged 2 pull requests for OAuth integration and has 3 PRs in active review. There were 4 new assignments and 8 comments focused on implementation details."

**Bad summary** (too technical):
> "This week, STORY-101 transitioned from status ID 10001 to 10002. User john.doe was assigned to STORY-102. Commit SHA abc123 was pushed to PR #145..."

**Focus on**:
- High-level progress (stories moved, completed)
- PR/MR activity (merged, in review, commit volume)
- Team activity (assignments, discussions)
- Notable trends (if detectable)

**Avoid**:
- Listing every ticket key
- Commit SHAs or technical identifiers
- Implementation details
- Individual developer names (use "the team")

**PR/MR Details to Include**:
- Number merged vs in review
- PR titles (if descriptive, e.g., "OAuth integration")
- Significant milestones (e.g., "first PR merged this epic")
- Overall commit volume (e.g., "18 commits this week")

**PR/MR Details to Exclude**:
- Commit messages
- Code review comments
- Individual file changes
- Specific reviewers

## Error Handling

- **No child issues**: Note "No active child issues" in summary
- **No activity**: "No significant activity this week"
- **Changelog unavailable**: Fall back to issue update dates
- **Comment fetch failed**: Skip comment count, note in log
- **Development info unavailable**: Not all Jira instances have GitHub/GitLab integration; skip PR/MR section
- **PR/MR API access denied**: May need GitHub/GitLab tokens; proceed without PR/MR data

## GitHub/GitLab Integration

### Jira Development Panel API

**Endpoint**: `/rest/dev-status/1.0/issue/detail?issueId={issueId}&applicationType={type}&dataType={dataType}`

**Supported integrations**:
- GitHub: `applicationType=github&dataType=pullrequest`
- GitLab: `applicationType=gitlab&dataType=mergerequest`
- Bitbucket: `applicationType=bitbucket&dataType=pullrequest`

**Response includes**:
- PR/MR URLs
- Status (open, merged, closed)
- Last updated timestamp
- Review status

### GitHub API (if direct access needed)

**Environment variables** (optional):
- `GITHUB_TOKEN` - GitHub personal access token
- `GITHUB_API_URL` - Default: https://api.github.com

**Endpoint**: `GET /repos/{owner}/{repo}/pulls/{number}`

**Fetch**:
- `state` (open, closed)
- `merged_at` (if merged)
- `updated_at` (filter by this)
- `commits` count
- `additions`, `deletions` (code churn)
- `reviews` count

### GitLab API (if direct access needed)

**Environment variables** (optional):
- `GITLAB_TOKEN` - GitLab personal access token
- `GITLAB_API_URL` - Default: https://gitlab.com/api/v4

**Endpoint**: `GET /projects/{id}/merge_requests/{iid}`

**Fetch**:
- `state` (opened, merged, closed)
- `merged_at` (if merged)
- `updated_at` (filter by this)
- `user_notes_count` (comments)

### Date Filtering

Only include PR/MR in summary if:
- `updated_at` >= (now - 7 days)
- OR `merged_at` >= (now - 7 days)

This ensures only recent PR/MR activity is included in weekly summary.

### Fallback: Parse URLs from Comments

If Jira development panel is unavailable:
1. Search issue comments for GitHub/GitLab URLs
2. Extract PR/MR numbers from URLs (e.g., `/pull/123`, `/merge_requests/456`)
3. Fetch details directly from GitHub/GitLab API
4. Filter by update date
Loading