Skip to content

Latest commit

 

History

History
534 lines (414 loc) · 13.9 KB

File metadata and controls

534 lines (414 loc) · 13.9 KB

Batch Operations & Queries

← Back to Index


Advanced search, analytics, and multi-item operations for efficient project management.

Table of Contents

  1. Advanced Search
  2. User-Focused Queries
  3. Sprint Analytics
  4. Dependency Analysis

Advanced Search

search_work_items

Advanced search with multiple filter criteria and name resolution.

Parameters:

  • projectId (number, optional): Project ID
  • titleContains (string, optional): Filter by title substring (case-insensitive)
  • descriptionContains (string, optional): Filter by description substring
  • categories (array, optional): Category names or IDs (['programming', 'bug'])
  • importanceLevels (array, optional): Priority names or IDs (['urgent', 'high'])
  • tags (array, optional): Tag names or IDs
  • hasAssignedUsers (boolean, optional): Filter by assignment status
  • isCompleted (boolean, optional): Filter by completion status
  • minEstimate (number, optional): Minimum estimated cost
  • maxEstimate (number, optional): Maximum estimated cost
  • boardId (number, optional): Filter by board/sprint
  • stageId (number, optional): Filter by workflow stage
  • milestoneId (number, optional): Filter by milestone
  • assignedUserId (number, optional): Filter by assigned user

Returns: { items: WorkItem[], total: number }

Examples

High-priority backend tasks:

const tasks = await callTool('search_work_items', {
  projectId: 230954,
  categories: ['programming'],  // Name resolution
  tags: ['backend'],
  importanceLevels: ['high', 'urgent'],
  minEstimate: 5,
  isCompleted: false
});

console.log(`Found ${tasks.total} high-priority backend tasks`);

Tasks nearing deadline:

const nearDeadline = await callTool('search_work_items', {
  projectId: 230954,
  boardId: 650299,  // Current sprint
  isCompleted: false,
  stageId: 2  // "In Progress"
});

Unassigned bugs:

const unassignedBugs = await callTool('search_work_items', {
  categories: ['bug'],
  hasAssignedUsers: false,
  isCompleted: false
});

Complex query:

// Find incomplete programming tasks:
// - Tagged as "backend" OR "api"
// - High or urgent priority
// - Estimated between 3-8 hours
// - NOT in closed sprints
const complexQuery = await callTool('search_work_items', {
  projectId: 230954,
  categories: ['programming'],
  tags: ['backend', 'api'],
  importanceLevels: ['high', 'urgent'],
  minEstimate: 3,
  maxEstimate: 8,
  isCompleted: false,
  boardId: 650299  // Active sprint only
});

User-Focused Queries

get_my_current_tasks

Get current user's assigned tasks with aggregated statistics.

Parameters:

  • projectId (number, optional): Project ID
  • boardId (number, optional): Filter by specific sprint
  • includeCompleted (boolean, optional): Include completed tasks (default: false)
  • limit (number, optional): Manual pagination limit
  • offset (number, optional): Manual pagination offset

Returns:

{
  items: WorkItem[];
  total: number;
  statistics: {
    byStage: { [stageName: string]: number };
    byPriority: { [priorityName: string]: number };
    totalEstimate: number;
  };
}

Examples

Daily standup view:

const result = await callTool('get_my_current_tasks', {
  projectId: 230954,
  includeCompleted: false
});

console.log(`You have ${result.total} active tasks:`);
console.log(`  In Progress: ${result.statistics.byStage['In Progress'] || 0}`);
console.log(`  In Review: ${result.statistics.byStage['Review'] || 0}`);
console.log(`  Total estimate: ${result.statistics.totalEstimate} hours`);

// Show top 3 tasks
result.items.slice(0, 3).forEach(task => {
  console.log(`  #${task.workItemId}: ${task.title} (${task.estimatedCost}h)`);
});

Sprint-specific tasks:

const sprintTasks = await callTool('get_my_current_tasks', {
  projectId: 230954,
  boardId: 650299,  // Current sprint
  includeCompleted: false
});

Weekly summary:

const weeklyWork = await callTool('get_my_current_tasks', {
  includeCompleted: true  // Include finished work
});

const completed = weeklyWork.items.filter(t => t.isCompleted);
const completedHours = completed.reduce((sum, t) => sum + (t.estimatedCost || 0), 0);

console.log(`Completed ${completed.length} tasks (${completedHours} hours)`);

get_incomplete_tasks

Get all incomplete work items for a project or board.

Parameters:

  • projectId (number, optional): Project ID
  • boardId (number, optional): Filter by board/sprint

Returns: { items, total }

Example:

const incomplete = await callTool('get_incomplete_tasks', {
  projectId: 230954,
  boardId: 650299
});

console.log(`Sprint has ${incomplete.total} incomplete tasks`);

get_backlog

Get TRUE backlog items - work items with NO board/sprint assignment.

⚠️ IMPORTANT: Returns ONLY unassigned items. Items in closed sprints are NOT backlog - they are archived historical work.

Parameters:

  • projectId (number, optional): Project ID

Returns: { items, total, note }

Example:

const backlog = await callTool('get_backlog', {
  projectId: 230954
});

console.log(`True backlog: ${backlog.total} items`);
console.log(`Note: ${backlog.note}`);

// Review unassigned items
backlog.items.forEach(item => {
  console.log(`#${item.workItemId}: ${item.title}`);
  console.log(`  Category: ${item.categoryName}`);
  console.log(`  Estimate: ${item.estimatedCost}h`);
});

Safety Note: Use this tool for backlog cleanup. Never delete items from closed sprints or completed work. See Deletion Safety Guide.


Sprint Analytics

get_active_sprint_overview

Get overview of currently active (open) sprints with task counts.

Parameters:

  • projectId (number, optional): Project ID

Returns:

{
  boards: Array<{
    boardId: number;
    name: string;
    startDate: string;
    endDate: string;
    totalTasks: number;
    completedTasks: number;
    progress: number;  // Percentage
  }>;
  totalActiveSprints: number;
}

Example:

const overview = await callTool('get_active_sprint_overview', {
  projectId: 230954
});

console.log(`${overview.totalActiveSprints} active sprints:\n`);

overview.boards.forEach(board => {
  const daysLeft = Math.ceil(
    (new Date(board.endDate) - new Date()) / (1000 * 60 * 60 * 24)
  );

  console.log(`${board.name}:`);
  console.log(`  Progress: ${board.progress}%`);
  console.log(`  Tasks: ${board.completedTasks}/${board.totalTasks}`);
  console.log(`  Days left: ${daysLeft}`);
  console.log('');
});

get_sprint_progress

Get comprehensive sprint progress metrics including burndown chart and hour tracking.

Parameters:

  • projectId (number, optional): Project ID
  • boardId (number, required): Board/sprint ID

Returns: Rich progress data with:

  • Burndown chart data: Daily progress snapshots
  • Dependency graph: Task relationships and blockers
  • Hour tracking: Logged vs estimated hours
  • Completion trends: Velocity and projections

Example:

const progress = await callTool('get_sprint_progress', {
  projectId: 230954,
  boardId: 650299
});

console.log('Sprint Progress Report:');
console.log(`  Total tasks: ${progress.totalWorkItems}`);
console.log(`  Completed: ${progress.completedWorkItems}`);
console.log(`  Completion: ${progress.completionPercentage}%`);
console.log(`  Hours logged: ${progress.loggedHours}/${progress.estimatedHours}`);

// Burndown data
if (progress.burndownData) {
  console.log('\nBurndown Chart:');
  progress.burndownData.forEach(day => {
    console.log(`  ${day.date}: ${day.remainingTasks} tasks remaining`);
  });
}

// Velocity calculation
const daysElapsed = progress.burndownData.length;
const velocity = progress.completedWorkItems / daysElapsed;
console.log(`\nVelocity: ${velocity.toFixed(1)} tasks/day`);

get_sprint_summary

Get compressed sprint status with task counts by stage (optimized for token usage).

Parameters:

  • projectId (number, optional): Project ID
  • boardId (number, required): Board/sprint ID

Returns: Compressed summary with minimal tokens:

{
  boardId: number;
  boardName: string;
  totalTasks: number;
  completedTasks: number;
  byStage: { [stageName: string]: number };
}

Example:

const summary = await callTool('get_sprint_summary', {
  boardId: 650299
});

console.log(`${summary.boardName} (${summary.completedTasks}/${summary.totalTasks})`);
Object.entries(summary.byStage).forEach(([stage, count]) => {
  console.log(`  ${stage}: ${count}`);
});

Use case: Quick sprint status checks without full analytics overhead.


Dependency Analysis

get_blockers

Analyze dependency graphs to identify blocking work items. Supports premium API + free-tier fallback.

Parameters:

  • projectId (number, optional): Project ID
  • workItemId (number|string, optional): Analyze specific work item (ID or title)
  • includeIndirect (boolean, optional): Include transitive dependencies (blockers of blockers). Default: false
  • useFallbackParser (boolean, optional): Use description parser if premium API unavailable (default: true)

Returns: Blocked items with dependency analysis

How It Works

Premium Plan:

  • Uses HacknPlan's native dependency API
  • Full graph analysis with transitive dependencies

Free Tier (Fallback):

  • Parses work item descriptions for dependency markers:
    • Blocked by #123
    • Depends on #456
    • Waiting for #789
  • Regex-based extraction

Examples

Find all blockers in project:

const blockers = await callTool('get_blockers', {
  projectId: 230954
});

console.log(`Found ${blockers.total} blocked items:`);

blockers.items.forEach(item => {
  console.log(`#${item.workItemId}: ${item.title}`);
  console.log(`  Blocked by: ${item.blockedBy.join(', ')}`);
});

Critical path for specific task:

const criticalPath = await callTool('get_blockers', {
  workItemId: 'Deploy to production',
  includeIndirect: true  // Include transitive blockers
});

console.log('Critical path:');
criticalPath.chain.forEach((step, i) => {
  console.log(`  ${i + 1}. #${step.workItemId}: ${step.title}`);
});

Identify bottlenecks:

const allBlockers = await callTool('get_blockers', {
  projectId: 230954,
  includeIndirect: false
});

// Find items blocking the most tasks
const blockingCounts = {};
allBlockers.items.forEach(item => {
  item.blockedBy.forEach(blockerId => {
    blockingCounts[blockerId] = (blockingCounts[blockerId] || 0) + 1;
  });
});

const bottlenecks = Object.entries(blockingCounts)
  .sort((a, b) => b[1] - a[1])
  .slice(0, 5);

console.log('Top bottlenecks (blocking most tasks):');
bottlenecks.forEach(([id, count]) => {
  console.log(`  Task #${id}: blocking ${count} other tasks`);
});

Common Patterns

Sprint Planning

// 1. Get current sprint status
const current = await callTool('get_sprint_summary', {
  boardId: 650299
});

// 2. Check backlog for next sprint
const backlog = await callTool('get_backlog', {
  projectId: 230954
});

// 3. Identify blockers before planning
const blockers = await callTool('get_blockers', {
  projectId: 230954
});

console.log(`Current sprint: ${current.completedTasks}/${current.totalTasks}`);
console.log(`Backlog: ${backlog.total} unassigned items`);
console.log(`Blockers: ${blockers.total} blocked items`);

Daily Standup Automation

// Get my tasks
const myTasks = await callTool('get_my_current_tasks', {
  includeCompleted: false
});

console.log(`📊 Daily Standup - ${new Date().toLocaleDateString()}\n`);

// Yesterday (completed)
const yesterday = myTasks.items.filter(t =>
  t.isCompleted &&
  new Date(t.updatedAt).toDateString() === new Date(Date.now() - 86400000).toDateString()
);

console.log(`✅ Completed yesterday (${yesterday.length}):`);
yesterday.forEach(t => console.log(`   - ${t.title}`));

// Today (in progress)
const today = myTasks.items.filter(t =>
  t.stageName === 'In Progress' && !t.isCompleted
);

console.log(`\n🔄 Working on today (${today.length}):`);
today.forEach(t => console.log(`   - ${t.title}`));

// Blockers
const myBlockers = await callTool('get_blockers', {
  projectId: 230954
});

const myBlockedTasks = myBlockers.items.filter(item =>
  myTasks.items.some(t => t.workItemId === item.workItemId)
);

if (myBlockedTasks.length > 0) {
  console.log(`\n🚫 Blockers (${myBlockedTasks.length}):`);
  myBlockedTasks.forEach(t => {
    console.log(`   - ${t.title} (blocked by: ${t.blockedBy.join(', ')})`);
  });
}

Sprint Retrospective

// Get sprint data
const progress = await callTool('get_sprint_progress', {
  boardId: 650299
});

console.log('📈 Sprint Retrospective\n');
console.log(`Total tasks: ${progress.totalWorkItems}`);
console.log(`Completed: ${progress.completedWorkItems} (${progress.completionPercentage}%)`);
console.log(`Velocity: ${(progress.completedWorkItems / progress.sprintDays).toFixed(1)} tasks/day`);
console.log(`Hours: ${progress.loggedHours}/${progress.estimatedHours} (${((progress.loggedHours / progress.estimatedHours) * 100).toFixed(0)}%)`);

// Identify patterns
const incomplete = progress.totalWorkItems - progress.completedWorkItems;
if (incomplete > progress.totalWorkItems * 0.3) {
  console.log(`\n⚠️ Warning: ${incomplete} incomplete tasks (>${30}% of sprint)`);
}

if (progress.loggedHours > progress.estimatedHours * 1.2) {
  console.log('\n⚠️ Sprint over-estimated by 20%+');
}

See Also: