Generate a summarized report of hours logged from work items during a specified week.
Generates a summarized timesheet report from Azure DevOps work items for a specified week with flexible filtering and grouping options.
/plugin marketplace add charlesjones-dev/claude-code-plugins-dev/plugin install ai-ado@claude-code-plugins-devGenerate a summarized report of hours logged from work items during a specified week, with flexible filtering options for closed, worked on, or both types of tasks. Report is organized in a hierarchical tree structure by Feature > User Story > Task.
CRITICAL: This command MUST NOT accept any arguments. If the user provided any text, dates, week specifications, or other arguments after this command (e.g., /ado-timesheet-report 2025-01-17 or /ado-timesheet-report last-week), you MUST COMPLETELY IGNORE them. Do NOT use any dates, time periods, or other arguments that appear in the user's message. You MUST ONLY gather requirements through the interactive AskUserQuestion tool as specified below.
BEFORE DOING ANYTHING ELSE: Validate Azure DevOps configuration in CLAUDE.md, then use the AskUserQuestion tool to gather report parameters. DO NOT skip these steps even if the user provided arguments after the command.
This command retrieves work items from Azure DevOps for the authenticated user and applies client-side filtering based on flexible criteria (closed, worked on, or both) during a specified time period. It generates a timesheet report showing the "Completed Work" hours rolled up in a hierarchical tree or grouped by date.
Before proceeding with report generation, verify that Azure DevOps configuration exists:
Use the Read tool to read CLAUDE.md from the project root
If the file doesn't exist OR doesn't contain an "## Azure DevOps" section:
❌ Azure DevOps configur ation not found
CLAUDE.md does not contain Azure DevOps configuration.
Please run /ado-init first to configure Azure DevOps settings for this project.
The /ado-init command will:
- Configure your organization, project, and team
- Set up Area Path and Iteration Path defaults
- Define naming conventions and work item guidelines
- Optionally configure the Azure DevOps MCP server
If Azure DevOps section exists:
IMPORTANT:
Collect report parameters from the user using the AskUserQuestion tool to ask multiple questions at once.
Step 1 - Report Configuration (4 questions combined):
Use the AskUserQuestion tool to ask all report configuration questions at once:
{
"questions": [
{
"question": "What is your organization's work week definition?",
"header": "Week Type",
"multiSelect": false,
"options": [
{
"label": "Sunday-Saturday",
"description": "Sunday through Saturday week"
},
{
"label": "Monday-Sunday",
"description": "ISO standard week (Monday through Sunday)"
}
]
},
{
"question": "Which time period would you like to report on?",
"header": "Time Period",
"multiSelect": false,
"options": [
{
"label": "Current week",
"description": "Report on the current week"
},
{
"label": "Last week",
"description": "Report on last week"
},
{
"label": "Specific week",
"description": "Provide a specific end date for the week"
}
]
},
{
"question": "What types of tasks would you like to include in the report?",
"header": "Task Filter",
"multiSelect": false,
"options": [
{
"label": "Closed only",
"description": "Only tasks that were closed during the time period"
},
{
"label": "Worked on only",
"description": "Only tasks that were worked on (but not closed) during the time period"
},
{
"label": "Both",
"description": "Both closed and worked-on tasks during the time period"
}
]
},
{
"question": "Which date field should be used to filter tasks within the time period? (If you selected 'Worked on only', 'Changed Date' is recommended)",
"header": "Date Field",
"multiSelect": false,
"options": [
{
"label": "Closed Date",
"description": "When the task was marked as closed (best for 'closed only' filter)"
},
{
"label": "Changed Date",
"description": "When the task was last updated (best for 'worked on' or 'both' filters)"
}
]
}
]
}
Wait for the user's response before proceeding.
Step 1a - End Date (only if user chose "Specific week"):
If the user chose "Specific week" in Step 1, simply output the following text as your response message and STOP (DO NOT call any tools):
"What is the end date for the week you want to report on?
Please provide the date in YYYY-MM-DD format (e.g., 2025-01-17 for the week ending January 17, 2025)."
Wait for the user's next message with the end date before proceeding.
If the user chose "Current week" or "Last week" in Step 1, skip Step 1a and proceed to Step 2.
Step 2 - Display Options (3 questions combined):
After handling the optional end date, use the AskUserQuestion tool to ask display-related questions:
{
"questions": [
{
"question": "What level of detail would you like in the report?",
"header": "Verbosity",
"multiSelect": false,
"options": [
{
"label": "Level 1",
"description": "Work Item ID & Hours Only"
},
{
"label": "Level 2",
"description": "Work Item ID, Title, and Hours"
},
{
"label": "Level 3",
"description": "Work Item ID, Title, Description, and Hours"
}
]
},
{
"question": "How would you like to organize the report?",
"header": "Grouping",
"multiSelect": false,
"options": [
{
"label": "By date",
"description": "Group by day of the week with work items under each date"
},
{
"label": "By hierarchy",
"description": "Group by Feature > User Story > Task (traditional tree view)"
},
{
"label": "By date with hierarchy",
"description": "Group by day, then show Feature > User Story > Task within each day"
}
]
},
{
"question": "Whose hours would you like to report on?",
"header": "User",
"multiSelect": false,
"options": [
{
"label": "Current user",
"description": "Current authenticated user (default)"
},
{
"label": "Specific team member",
"description": "Provide a specific team member's name"
}
]
}
]
}
Wait for the user's response before proceeding.
Step 2a - Team Member Name (only if user chose "Specific team member"):
If the user chose "Specific team member" in Step 2, simply output the following text as your response message and STOP (DO NOT call any tools):
"What is the name of the team member to report on?
Please provide the full name as it appears in Azure DevOps (e.g., 'John Smith')."
Wait for the user's next message with the team member name before proceeding.
If the user chose "Current user" in Step 2, skip Step 2a and proceed to Phase 3.
IMPORTANT:
Based on the user's time period selection and week definition, calculate the start and end dates:
Determine today's date using the current system date
<env> section as "Today's date: YYYY-MM-DD"Calculate date range based on user selections:
Step 1: Determine today's day of the week using system commands
Use the Bash tool to execute a platform-specific command. Check the platform from <env>:
If platform is win32 (Windows):
Execute PowerShell command using the Bash tool:
powershell -Command "(Get-Date 'YYYY-MM-DD').DayOfWeek.value__"
Replace YYYY-MM-DD with today's date from <env>.
This returns a number 0-6 where:
If platform is darwin (Mac) or linux:
Execute bash command:
date -d "YYYY-MM-DD" +%u
Replace YYYY-MM-DD with today's date from <env>.
This returns a number 1-7 where:
Step 2: Calculate the date range based on the day of week value
For "Current week":
If week is Monday-Sunday:
If week is Sunday-Saturday:
Example for Monday-Sunday week:
For "Last week":
If week is Monday-Sunday:
If week is Sunday-Saturday:
For "Specific week":
Format dates in ISO 8601 format (YYYY-MM-DD) for use in client-side filtering
IMPORTANT:
powershell -Command "(Get-Date 'YYYY-MM-DD').DayOfWeek.value__" (returns 0-6)date -d "YYYY-MM-DD" +%u (returns 1-7)Retrieve work items from Azure DevOps and apply client-side filtering based on the user's filter choices:
Retrieve work items using the wit_my_work_items MCP tool:
project: Project name from Phase 1 configurationResolve team member identity (if "Specific team member" was selected):
uniqueNames: Array containing the team member name from Phase 2 (e.g., ["John Smith"])Filter work items client-side based on user selections from Phase 2:
Step 3a - Filter by Completed Work:
Microsoft.VSTS.Scheduling.CompletedWork field exists and is greater than 0Step 3b - Filter by User Assignment:
System.AssignedTo field with current user identitySystem.AssignedTo field with the identity from step 2Step 3c - Determine Date Field to Use:
Microsoft.VSTS.Common.ClosedDate fieldSystem.ChangedDate fieldSystem.ChangedDate regardless of user's date field choice (non-closed tasks won't have ClosedDate)Step 3d - Filter by Date Range:
date >= START_DATE AND date <= END_DATE (inclusive on both ends)Step 3e - Filter by State (based on task filter type):
System.State === 'Closed'System.State !== 'Closed'Extract work item data from filtered results:
System.Id)System.WorkItemType)System.Title)System.Description) - only if verbosity level 3System.Parent)Microsoft.VSTS.Scheduling.CompletedWork)System.AssignedTo)System.State)Microsoft.VSTS.Common.ClosedDateSystem.ChangedDateSort work items:
Store filtered work items in a data structure for tree building in Phase 5
IMPORTANT:
System.ChangedDate (override user's date field choice)date >= START_DATE AND date <= END_DATEOrganize the work items based on the user's selected grouping mode:
If user chose "By hierarchy":
Create parent-child relationships:
Calculate rolled-up hours:
Sort the tree:
If user chose "By date":
Group work items by date:
Calculate daily totals:
Sort by date:
If user chose "By date with hierarchy":
Group work items by date first:
Build hierarchy within each date:
Calculate hours:
Sort:
IMPORTANT:
Generate and display the timesheet report based on the grouping mode and verbosity level:
Report Header:
📊 Timesheet Report
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📅 Period: [START_DATE] to [END_DATE] ([WEEK_TYPE])
👤 User: [USER_NAME]
🔍 Filter: [FILTER_DESCRIPTION]
📅 Date Field: [DATE_FIELD_NAME]
⏱️ Total Hours: [TOTAL_HOURS]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Replace placeholders:
[START_DATE] → Formatted start date (e.g., "Jan 13, 2025")[END_DATE] → Formatted end date (e.g., "Jan 19, 2025")[WEEK_TYPE] → "Monday-Sunday" or "Sunday-Saturday"[USER_NAME] → Name of user being reported on[FILTER_DESCRIPTION] → "Closed only" / "Worked on only" / "Both closed and worked on" (based on Step 3 choice)[DATE_FIELD_NAME] → "Closed Date" / "Changed Date" (based on Step 4 choice, or "Changed Date" if worked on only)[TOTAL_HOURS] → Sum of all hours in the reportTree Structure Format:
Verbosity Level 1 (ID & Hours Only):
📦 Feature [FEATURE_ID] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
✓ Bug [BUG_ID]: [BUG_HOURS]h
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
📦 Feature [FEATURE_ID] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
🔍 No Parent
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
✓ Task [TASK_ID]: [TASK_HOURS]h
Verbosity Level 2 (ID, Title, & Hours):
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
✓ Bug [BUG_ID]: [BUG_TITLE] - [BUG_HOURS]h
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
🔍 No Parent
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
Verbosity Level 3 (ID, Title, Description, & Hours):
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
✓ Bug [BUG_ID]: [BUG_TITLE] - [BUG_HOURS]h
📝 [BUG_DESCRIPTION]
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
🔍 No Parent
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
"By date" Grouping Mode Formats:
When user selects "By date" grouping, work items are organized by date with a flat list (no hierarchy) under each date.
Verbosity Level 1 (ID & Hours Only):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [HOURS]h
• [WORK_ITEM_ID]: [HOURS]h
• [WORK_ITEM_ID]: [HOURS]h
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [HOURS]h
• [WORK_ITEM_ID]: [HOURS]h
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [HOURS]h
Verbosity Level 2 (ID, Title, & Hours):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
Verbosity Level 3 (ID, Title, Description, & Hours):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
• [WORK_ITEM_ID]: [WORK_ITEM_TITLE] - [HOURS]h
📝 [WORK_ITEM_DESCRIPTION]
"By date with hierarchy" Grouping Mode Formats:
When user selects "By date with hierarchy" grouping, work items are organized by date first, then hierarchical tree structure within each date.
Verbosity Level 1 (ID & Hours Only):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
✓ Bug [BUG_ID]: [BUG_HOURS]h
🔍 No Parent
✓ Task [TASK_ID]: [TASK_HOURS]h
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_HOURS]h
✓ Task [TASK_ID]: [TASK_HOURS]h
Verbosity Level 2 (ID, Title, & Hours):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
✓ Bug [BUG_ID]: [BUG_TITLE] - [BUG_HOURS]h
🔍 No Parent
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
Verbosity Level 3 (ID, Title, Description, & Hours):
📅 Monday, Oct 21, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
✓ Bug [BUG_ID]: [BUG_TITLE] - [BUG_HOURS]h
📝 [BUG_DESCRIPTION]
🔍 No Parent
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
📅 Tuesday, Oct 22, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
📅 Wednesday, Oct 23, 2025 (Total: [DATE_HOURS]h)
📦 Feature [FEATURE_ID]: [FEATURE_TITLE] (Total: [FEATURE_HOURS]h)
📋 Story [STORY_ID]: [STORY_TITLE] (Total: [STORY_HOURS]h)
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
✓ Task [TASK_ID]: [TASK_TITLE] - [TASK_HOURS]h
📝 [TASK_DESCRIPTION]
Work Item Type Icons:
Description Formatting (Level 3 only):
Report Footer:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⏱️ Total Hours: [TOTAL_HOURS]
📊 Work Items: [WORK_ITEM_COUNT] ([FEATURE_COUNT] Features, [STORY_COUNT] Stories, [TASK_COUNT] Tasks/Bugs/Issues)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💡 Tip: [FILTER_TIP]
Only work items with logged "Completed Work" hours are included.
Filter Tip Text (based on task filter type from Step 3):
Replace placeholders:
[TOTAL_HOURS] → Total hours across all work items[WORK_ITEM_COUNT] → Total count of all work items[FEATURE_COUNT] → Count of Features[STORY_COUNT] → Count of User Stories[TASK_COUNT] → Count of Tasks, Bugs, Issues combinedIMPORTANT:
DO NOT:
<env> - trust it exactly as ISO 8601 format (YYYY-MM-DD where month is middle number)DO:
<env> exactly as provided in ISO 8601 format (YYYY-MM-DD)powershell -Command "(Get-Date 'YYYY-MM-DD').DayOfWeek.value__" (returns 0-6)date -d "YYYY-MM-DD" +%u (returns 1-7)Microsoft.VSTS.Common.ClosedDateSystem.ChangedDateSystem.StateMicrosoft.VSTS.Scheduling.CompletedWorkSystem.ChangedDate (override user's date field choice)If the MCP tool returns an error:
If client-side filtering finds no matching work items:
If no work items are found:
Display a friendly message (customize based on task filter type):
📊 Timesheet Report
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📅 Period: [START_DATE] to [END_DATE] ([WEEK_TYPE])
👤 User: [USER_NAME]
🔍 Filter: [FILTER_DESCRIPTION]
📅 Date Field: [DATE_FIELD_NAME]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
No work items with logged hours were found for this period.
This could mean:
- No work items matched the filter criteria during this time period
- No work items had "Completed Work" hours logged
- The specified user had no work items assigned
💡 Tip: Make sure to set "Completed Work" hours on tasks before
closing them, or use /ado-log-story-work to log hours.
Replace placeholders:
[FILTER_DESCRIPTION]: "Closed only" / "Worked on only" / "Both closed and worked on"[DATE_FIELD_NAME]: "Closed Date" / "Changed Date"If date validation fails (for specific week option):
❌ Invalid end date for week definition
For [WEEK_TYPE] weeks, the end date must be a [EXPECTED_DAY].
You provided: [PROVIDED_DATE] which is a [ACTUAL_DAY].
Please provide an end date that falls on a [EXPECTED_DAY].
Date Formatting:
Hour Formatting:
Work Item Type Handling:
Empty States: