/pm-status-report
Stakeholder-grade comprehensive status report for a Huly tracker project. ~10 sections including an exhaustive component-grouped task list. Read-only, no writes to Huly.
Requires the huly MCP server.
Args
/pm-status-report <PROJECT> [--period 7d|30d|sprint]
PROJECT— identifier or_id. Required.--period— reporting window:7d(default) — last 7 days30d— last 30 dayssprint— auto-detect the active milestone (status == 1); use its start (createdOnof milestone) →target_dateas the window
Tools called
Run in two waves. Wave 1 (parallel) gathers the dataset; wave 2 fans out for relations / supplementary sets.
Wave 1:
huly_get_project{ project }— name, identifier, archived flag.huly_list_statuses{ project }—_id→ name + category map; defines the status column order for the breakdown.huly_list_components{ project }—_id→ label map.huly_list_issues{ project, limit: 500 }— main dataset.huly_list_milestones{ project }— full milestone state.huly_get_detailed_time_report{ project, from: <period start> }— effort in period, grouped by issue + assignee.huly_list_time_spend_reports{ project, from: now − 8 weeks }— velocity trend.huly_list_documents(only if a teamspace is known — typically skipped unless the user provided one) for doc coverage.huly_list_test_runs— only if a QMS test project shares the project's name; otherwise skip.
Wave 2:
- For each in-progress issue: nothing — instead, compute
blockscounts in a single local pass (see "Bottleneck computation" below) over the wave-1 dataset to avoid 500× calls.
Local computation
Lookup maps
statusMap: id → { name, category, rank }— preservehuly_list_statusesorder for column rendering.componentMap: id → labelplus a synthetic"__uncategorized__" → "Uncategorized"bucket.
Group issues by component, then by status
for issue in issues:
comp = componentMap[issue.component] or "Uncategorized"
status = statusMap[issue.status].name
buckets[comp][status].append(issue)
Within each (comp, status) bucket, sort by:
- Priority (urgent → low → none)
modifiedOndesc
Bottleneck count
blocksByIssue = {} # issue._id → list of blocked issue identifiers
for issue in issues:
for rel in issue.relations or []:
if rel._class == "tracker:relation:Blocks":
blocksByIssue.setdefault(rel.relatedTo, []).append(issue.identifier)
Top bottlenecks = those with len(blockers) >= 2, sorted by descending count.
Velocity
- Bucket
huly_list_time_spend_reportsentries into 7-day windows for the last 8 weeks based ondate. - Count of issues closed per week = issues whose
modifiedOnfalls in week ANDstatusMap[issue.status].category == "tracker:statusCategory:Completed".
Team performance
For each assignee (group by assignee):
- Active = open issues currently assigned (status not done/cancelled).
- Closed in period = issues closed (by
modifiedOn+ done category) in the period. - Time in period = sum of report.value where
report.createdBy == assigneeanddate >= period_start. - Avg cycle = mean
(modifiedOn - createdOn)/86_400_000over period-closed issues. - On-time = % of period-closed issues where
dueDate == null OR modifiedOn <= dueDate.
Risks
Reuse the rules from pm-risks (see that SKILL.md). Show only the counts and top 3 in this report; full detail belongs in /pm-risks.
Recommendations
Derive 3–5 lines from:
- Top HIGH risk → "Assign / re-scope / unblock …"
- Largest stale component → "Review status of
<Component>— N issues stale" - Worst on-time assignee → "Check in with
<name>(on-time %)" - Documentation staleness (if doc data available) → "Refresh
<doc>(Nd stale)"
Output structure
═══════════════════════════════════════════════════════
PROJECT STATUS REPORT — [Name] ([IDENT])
Period: [YYYY-MM-DD] → [YYYY-MM-DD] ([period label])
Generated: [YYYY-MM-DD]
═══════════════════════════════════════════════════════
EXECUTIVE SUMMARY
─────────────────
[2–3 sentence narrative covering: this-period delivery vs prior, active
sprint state, top 1–2 risks.]
KEY METRICS
───────────
This period Prev period Δ
Issues created [n] [n] [±%]
Issues closed [n] [n] [±%]
Net backlog change [±n] [±n] —
Time logged (hours) [h] [h] [±%]
Active contributors [n] [n] [±%]
Avg cycle time (days) [d] [d] [±%]
SCOPE & PROGRESS
────────────────
Total issues: [N]
├─ Done: [n] ([%])
├─ In-progress: [n] ([%])
├─ In-review: [n] ([%])
├─ Backlog: [n] ([%])
└─ Cancelled: [n] ([%])
Scope by priority:
🔥 Urgent [n] ([n] unassigned ⚠ if >0)
⬆ High [n] ([n] in-progress)
→ Medium [n]
⬇ Low [n]
— None [n]
MILESTONES
──────────
✓ [label] completed [date] ([done]/[total] done, 100%)
◐ [label] target [date], [N]d left
[total] issues · [done] done · [open] open · [%]%
Required pace: [r]/day · Actual: [a]/day
[⚠ AT RISK — recommend re-scope] if applicable
○ [label] target [date], [N]d left, [done]/[total]
VELOCITY (last 8 weeks — issues closed)
───────────────────────────────────────
Week of [date]: [bar] [n]
...
Week of [date]: [bar] [n] ← current
Trend: [±n] issues/week ([direction])
TEAM PERFORMANCE
────────────────
Member Active Closed/wk Time/wk Avg cycle On-time
[name] [n] [n] [h]h [d]d [%]
...
unassigned [n] — — — —
[⚠ flags for: high unassigned count, low on-time per member, no time logged]
═══════════════════════════════════════════════════════
ALL TASKS BY COMPONENT (full breakdown)
═══════════════════════════════════════════════════════
COMPONENT: [label] ([N] issues)
──────────────────────────────────────────────────────
[Status name 1] ([n])
[PROJ-N] [priority icon] [priority label] [title] @[assignee] [age info]
...
[Status name 2] ([n])
...
COMPONENT: [next label] ([N] issues)
──────────────────────────────────────────────────────
...
UNCATEGORIZED ([N] issues)
──────────────────────────────────────────────────────
...
═══════════════════════════════════════════════════════
BOTTLENECKS
───────────
[PROJ-N] [title] blocks [n] · [stuck Nd|in-review|...] · @[assignee]
...
RISKS (see /pm-risks for full detail)
─────
🔴 [n] HIGH [top 1–2, comma-separated]
🟡 [n] MEDIUM [counts only]
🟢 [n] WATCH [counts only]
QUALITY (only if test data was found)
───────
Latest run: [name] · [n] cases · [n] passed · [n] failed · [n] blocked
Pass rate: [%] (target [%])
Open bugs by priority: urgent [n] · high [n] · medium [n]
COMPONENTS (activity snapshot)
──────────
[label] [n] open · last activity [Nd]
...
DOCUMENTATION (only if doc data available)
─────────────
[n] linked teamspaces · [n] documents · [n] updated this period
[⚠ flags for stale docs >30d]
RECOMMENDATIONS
───────────────
1. [derived line]
2. [derived line]
3. [derived line]
Row format inside "All Tasks by Component"
[IDENTIFIER] [icon] [priority] [title (truncate to 50 chars + … if longer)] @[assignee or —] [age]
- Priority icons: 🔥 urgent · ⬆ high · → med · ⬇ low · — none.
- Age column:
- For non-done/cancelled: number of days since
modifiedOn, e.g.2d. If > 7d and in-progress, append ⚠. - For Done:
closed [MM-DD]. - For Cancelled:
cancelled [MM-DD].
- For non-done/cancelled: number of days since
- Assignee: prefer display name from
huly_workspace_infomembers lookup; fall back to email;—if null.
Output sizing
- Status order within each component follows the project's status order (from
huly_list_statuses). - Show every issue in every status bucket — no truncation, this is the comprehensive view.
- If project > 500 issues: show a warning at the top of the report:
⚠ Showing first 500 issues by modifiedOn — N additional issues omitted. Filter by milestone or component to narrow.
Edge cases
- No active milestone for
--period sprint— fall back to--period 7dand add a note:No active sprint detected; using 7d window. - Project with no components — the entire breakdown collapses to a single
UNCATEGORIZEDsection. That's fine. - No prior period for delta arithmetic — show
—in Δ column rather than 100%. - Status with zero issues in a component — omit that status sub-bucket entirely (don't print
Done (0)). - Long component label — wrap to 30 chars in the header line.
Don't
- Don't write anything to Huly (no docs, no comments, no field updates).
- Don't truncate the by-component task list — the user explicitly wants the full breakdown.
- Don't dump raw JSON.
- Don't call
huly_list_issue_relationsper-issue — compute theblocksmap locally from the dataset. - Don't include sections whose underlying data was empty (e.g. omit QUALITY entirely if no test runs found, omit DOCUMENTATION if no teamspace was supplied).