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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"name": "jira",
"source": "./plugins/jira",
"description": "A plugin to automate tasks with Jira",
"version": "0.4.5"
"version": "0.4.6"
},
{
"name": "ci",
Expand Down
2 changes: 1 addition & 1 deletion docs/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@
"name": "Jira Status Analysis Engine"
}
],
"version": "0.4.5"
"version": "0.4.6"
},
{
"commands": [
Expand Down
2 changes: 1 addition & 1 deletion plugins/jira/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "jira",
"description": "A plugin to automate tasks with Jira",
"version": "0.4.5",
"version": "0.4.6",
"author": {
"name": "github.com/openshift-eng"
}
Expand Down
39 changes: 16 additions & 23 deletions plugins/jira/commands/update-weekly-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,32 +156,24 @@ For each issue listed in the manifest:

##### a. Read Pre-Gathered Data

Read the issue's JSON file from `.work/weekly-status/{date}/issues/{ISSUE-KEY}.json`
Use the `summarize_issue.py` script to extract a structured, human-readable summary from the pre-gathered JSON. This avoids reading large JSON files directly (which can exceed context limits) and produces output optimized for LLM analysis.

The file contains:

```json
{
"issue": {
"key": "OCPSTRAT-1234",
"summary": "...",
"status": "In Progress",
"assignee": {"email": "...", "name": "..."},
"current_status_summary": "...",
"last_status_summary_update": "..."
},
"descendants": {
"total": 15,
"by_status": {"Closed": 5, "In Progress": 8, "To Do": 2},
"updated_in_range": [...],
"completion_pct": 33.3
},
"changelog_in_range": [...],
"comments_in_range": [...],
"prs": [...]
}
```bash
python3 {plugins-dir}/jira/skills/status-analysis/scripts/summarize_issue.py \
{ISSUE-KEY} --date-dir .work/weekly-status/{date}
```

The script outputs a structured summary with sections for:
- Issue header (key, summary, status, assignee, current status summary)
- Descendants (total, completion %, by-status breakdown, updated in range)
- Changelog entries (formatted as `date by author: field: old -> new`)
- Comments (non-bot only, with body truncated to 300 chars)
- PRs (active in range, merged this week, open)

**Script location**: `plugins/jira/skills/status-analysis/scripts/summarize_issue.py`

The underlying JSON file is at `.work/weekly-status/{date}/issues/{ISSUE-KEY}.json` if you need to read additional details not shown in the summary.

##### b. Analyze Activity (using Status Analysis Engine)

Using the pre-gathered data, apply the activity analysis rules from `activity-analysis.md`:
Expand Down Expand Up @@ -546,4 +538,5 @@ See the Jira plugin's skills directory for examples of project-specific customiz

- **Shared skill**: `plugins/jira/skills/status-analysis/SKILL.md`
- **Data gatherer script**: `plugins/jira/skills/status-analysis/scripts/gather_status_data.py`
- **Issue summarizer script**: `plugins/jira/skills/status-analysis/scripts/summarize_issue.py`
- **Single-issue rollup**: `/jira:status-rollup` - Generate comprehensive status comment for one issue
20 changes: 14 additions & 6 deletions plugins/jira/skills/status-analysis/scripts/gather_status_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ def _build_pr_query(self, pr_refs: List[PRRef]) -> str:
reviewDecision
reviews(last: 30) {{
nodes {{
author {{ login }}
author {{ login ... on User {{ name }} }}
state
body
submittedAt
Expand All @@ -562,7 +562,7 @@ def _build_pr_query(self, pr_refs: List[PRRef]) -> str:
isResolved
comments(first: 5) {{
nodes {{
author {{ login }}
author {{ login ... on User {{ name }} }}
body
createdAt
path
Expand All @@ -577,7 +577,7 @@ def _build_pr_query(self, pr_refs: List[PRRef]) -> str:
oid
messageHeadline
committedDate
author {{ email }}
author {{ email name }}
}}
}}
}}
Expand Down Expand Up @@ -833,8 +833,10 @@ def _filter_pr_to_range(
for review in (pr_data.get("reviews", {}).get("nodes") or []):
submitted = self._parse_datetime(review.get("submittedAt"))
if submitted and date_range.contains(submitted):
author_obj = review.get("author") or {}
reviews_in_range.append({
"author": (review.get("author") or {}).get("login", "Unknown"),
"author": author_obj.get("login", "Unknown"),
"author_name": author_obj.get("name") or author_obj.get("login", "Unknown"),
"state": review.get("state"),
"body": (review.get("body") or "")[:500],
"submitted_at": review.get("submittedAt"),
Expand All @@ -846,11 +848,13 @@ def _filter_pr_to_range(
commit = node.get("commit", {})
committed = self._parse_datetime(commit.get("committedDate"))
if committed and date_range.contains(committed):
commit_author = commit.get("author") or {}
commits_in_range.append({
"sha": (commit.get("oid") or "")[:7],
"message": commit.get("messageHeadline", ""),
"date": commit.get("committedDate"),
"author": (commit.get("author") or {}).get("email", "Unknown"),
"author": commit_author.get("email", "Unknown"),
"author_name": commit_author.get("name") or commit_author.get("email", "Unknown"),
})

# Filter review comments
Expand All @@ -859,8 +863,10 @@ def _filter_pr_to_range(
for comment in (thread.get("comments", {}).get("nodes") or []):
created = self._parse_datetime(comment.get("createdAt"))
if created and date_range.contains(created):
comment_author = comment.get("author") or {}
review_comments_in_range.append({
"author": (comment.get("author") or {}).get("login", "Unknown"),
"author": comment_author.get("login", "Unknown"),
"author_name": comment_author.get("name") or comment_author.get("login", "Unknown"),
"path": comment.get("path"),
"line": comment.get("line"),
"body": (comment.get("body") or "")[:300],
Expand Down Expand Up @@ -1216,6 +1222,7 @@ def _build_manifest(

# Build issue data
assignee = fields.get("assignee") or {}
labels = [l for l in fields.get("labels", []) if isinstance(l, str)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Rename the one-letter loop variable to avoid Ruff E741.

Line 1225 uses l, which is flagged as ambiguous and may fail lint checks.

Suggested fix
-            labels = [l for l in fields.get("labels", []) if isinstance(l, str)]
+            labels = [label for label in fields.get("labels", []) if isinstance(label, str)]
🧰 Tools
🪛 Ruff (0.15.13)

[error] 1225-1225: Ambiguous variable name: l

(E741)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/jira/skills/status-analysis/scripts/gather_status_data.py` at line
1225, The list comprehension creating labels uses a single-letter loop variable
`l` which triggers Ruff E741; change the loop variable in that comprehension to
a descriptive name like `label` (e.g., labels = [label for label in
fields.get("labels", []) if isinstance(label, str)]) so the comprehension in
gather_status_data.py that references fields.get("labels", []) uses a
non-ambiguous identifier.

issue_data = {
"issue": {
"key": issue_key,
Expand All @@ -1225,6 +1232,7 @@ def _build_manifest(
"email": assignee.get("emailAddress"),
"name": assignee.get("displayName"),
},
"labels": labels,
"current_status_summary": fields.get(self.config.status_summary_field),
"last_status_summary_update": last_status_update,
},
Expand Down
Loading
Loading