From 7058eb2e509004215524a9d5afc409358786223c Mon Sep 17 00:00:00 2001 From: Hugo Nijhuis Date: Tue, 13 Jan 2026 23:45:55 +0100 Subject: [PATCH] feat(dashboard): add dashboard skill for milestone/issue overview Co-Authored-By: Claude Opus 4.5 --- skills/dashboard/SKILL.md | 47 +++++++++++ .../dashboard/scripts/generate-dashboard.sh | 83 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 skills/dashboard/SKILL.md create mode 100755 skills/dashboard/scripts/generate-dashboard.sh diff --git a/skills/dashboard/SKILL.md b/skills/dashboard/SKILL.md new file mode 100644 index 0000000..6c38235 --- /dev/null +++ b/skills/dashboard/SKILL.md @@ -0,0 +1,47 @@ +--- +name: dashboard +description: > + Display milestones with unblocked issues at a glance. + Use when you want to see project progress and which issues are ready to work on. + Invoke with /dashboard [milestone-name-filter] +model: claude-haiku-4-5 +user-invocable: true +context: fork +allowed-tools: + - Bash(~/.claude/skills/dashboard/scripts/generate-dashboard.sh*) +--- + +# Dashboard + +@~/.claude/skills/gitea/SKILL.md + +Display all milestones and their unblocked issues. Issues are considered unblocked if they have no open blockers in their dependency list. + +## Workflow + +1. **Run the dashboard script** with the milestone filter argument (if provided): + ```bash + ~/.claude/skills/dashboard/scripts/generate-dashboard.sh "$1" + ``` + +2. **Display the output** to the user + +The script automatically: +- Fetches all milestones from the repository +- For each milestone, gets all open issues +- For each issue, checks dependencies with `tea issues deps list` +- Categorizes issues as unblocked (no open dependencies) or blocked (has open dependencies) +- Displays results grouped by milestone in this format: + +``` +## Milestone: Release 1.0 + +✓ Unblocked (3): + #42 Implement feature X + #43 Fix bug in Y + #45 Add tests for Z + +⊘ Blocked (2): + #40 Feature A (blocked by #39) + #41 Feature B (blocked by #38, #37) +``` diff --git a/skills/dashboard/scripts/generate-dashboard.sh b/skills/dashboard/scripts/generate-dashboard.sh new file mode 100755 index 0000000..2b524f9 --- /dev/null +++ b/skills/dashboard/scripts/generate-dashboard.sh @@ -0,0 +1,83 @@ +#!/bin/bash +set -euo pipefail + +# Generate dashboard showing milestones with unblocked/blocked issues +# Usage: ./generate-dashboard.sh [milestone-filter] + +MILESTONE_FILTER="${1:-}" + +# Get all milestones +milestones_json=$(tea milestones -o json) + +# Parse milestone names +milestone_names=$(echo "$milestones_json" | jq -r '.[].title') + +# Process each milestone +while IFS= read -r milestone; do + # Skip if filter provided and doesn't match + if [[ -n "$MILESTONE_FILTER" && ! "$milestone" =~ $MILESTONE_FILTER ]]; then + continue + fi + + echo "## Milestone: $milestone" + echo "" + + # Get open issues for this milestone + issues_json=$(tea issues --milestones "$milestone" --state open -o json 2>/dev/null || echo "[]") + + # Skip empty milestones or invalid JSON + issue_count=$(echo "$issues_json" | jq -e 'length' 2>/dev/null || echo "0") + if [[ "$issue_count" -eq 0 ]]; then + echo "No open issues" + echo "" + continue + fi + + # Arrays for categorizing issues + declare -a unblocked=() + declare -a blocked=() + + # Process each issue + while IFS=$'\t' read -r number title; do + # Check dependencies (tea returns plain text "Issue #N has no dependencies" when empty) + deps_output=$(tea issues deps list "$number" -o json 2>/dev/null || echo "") + + # If output contains "has no dependencies", treat as empty array + if [[ "$deps_output" == *"has no dependencies"* ]]; then + deps_json="[]" + else + deps_json="$deps_output" + fi + + # Count open dependencies + open_deps=$(echo "$deps_json" | jq -r '[.[] | select(.state == "open")] | length' 2>/dev/null || echo "0") + + if [[ "$open_deps" -eq 0 ]]; then + # No open blockers - unblocked + unblocked+=("#$number $title") + else + # Has open blockers - blocked + blocker_list=$(echo "$deps_json" | jq -r '[.[] | select(.state == "open") | "#\(.index)"] | join(", ")') + blocked+=("#$number $title (blocked by $blocker_list)") + fi + done < <(echo "$issues_json" | jq -r '.[] | [.index, .title] | @tsv') + + # Display unblocked issues + echo "✓ Unblocked (${#unblocked[@]}):" + if [[ ${#unblocked[@]} -eq 0 ]]; then + echo " (none)" + else + printf ' %s\n' "${unblocked[@]}" + fi + echo "" + + # Display blocked issues + echo "⊘ Blocked (${#blocked[@]}):" + if [[ ${#blocked[@]} -eq 0 ]]; then + echo " (none)" + else + printf ' %s\n' "${blocked[@]}" + fi + echo "" + +done <<< "$milestone_names"