feat(dashboard): add dashboard skill for milestone/issue overview

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 23:45:55 +01:00
parent f99f8f072e
commit 7058eb2e50
2 changed files with 130 additions and 0 deletions

47
skills/dashboard/SKILL.md Normal file
View File

@@ -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)
```

View File

@@ -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"