Skip to content

Latest commit

Β 

History

History
503 lines (391 loc) Β· 11.4 KB

File metadata and controls

503 lines (391 loc) Β· 11.4 KB

Common Patterns

← Back to Index


Real-world recipes and workflows for common HacknPlan tasks.

Table of Contents

  1. Feature Implementation
  2. Sprint Management
  3. Daily Workflows
  4. Task Management
  5. Dependency Tracking
  6. Pagination

Feature Implementation

Create Feature with Subtasks

Complete workflow for breaking down a user story into subtasks.

// 1. Create parent user story
const story = await callTool('create_work_items', {
  projectId: 230954,
  items: [{
    title: 'User Authentication System',
    categoryId: 'programming',
    description: 'Complete authentication with OAuth2 and JWT',
    estimatedCost: 40,
    boardId: 650299
  }]
});

const storyId = story.items[0].workItemId;

// 2. Create subtasks
await callTool('create_work_items', {
  projectId: 230954,
  items: [
    {
      title: 'Design database schema',
      categoryId: 'programming',
      estimatedCost: 4,
      parentStoryId: storyId
    },
    {
      title: 'Implement OAuth2 flow',
      categoryId: 'programming',
      estimatedCost: 12,
      parentStoryId: storyId,
      tagIds: ['backend', 'oauth']
    },
    {
      title: 'Add JWT token handling',
      categoryId: 'programming',
      estimatedCost: 8,
      parentStoryId: storyId,
      tagIds: ['backend', 'security']
    },
    {
      title: 'Write authentication tests',
      categoryId: 'programming',
      estimatedCost: 6,
      parentStoryId: storyId,
      tagIds: ['testing']
    },
    {
      title: 'Document API endpoints',
      categoryId: 'writing',
      estimatedCost: 3,
      parentStoryId: storyId
    }
  ]
});

Total estimate: 40h (parent) = sum of subtasks (4+12+8+6+3)


Sprint Management

Sprint Setup

Complete sprint planning workflow:

// 1. Create sprint board
const sprint = await callTool('create_boards', {
  projectId: 230954,
  boards: [{
    name: 'Sprint 4: Authentication & Security',
    startDate: '2026-01-01T00:00:00Z',
    endDate: '2026-01-14T23:59:59Z',
    description: 'Focus on security features and OAuth implementation'
  }]
});

const sprintId = sprint.items[0].boardId;

// 2. Create milestone for this quarter
const milestone = await callTool('create_milestone', {
  projectId: 230954,
  name: 'Q1 2026 Release',
  dueDate: '2026-03-31T23:59:59Z',
  description: 'First quarterly release with core features'
});

const milestoneId = milestone.milestoneId;

// 3. Get backlog items
const backlog = await callTool('get_backlog', {
  projectId: 230954
});

// 4. Select items for sprint (top 10 by priority)
const selectedItems = backlog.items
  .sort((a, b) => a.importanceLevelId - b.importanceLevelId)
  .slice(0, 10);

// 5. Move to sprint and assign milestone
await callTool('update_work_items', {
  projectId: 230954,
  items: selectedItems.map(item => ({
    workItemId: item.workItemId,
    boardId: sprintId,
    milestoneId: milestoneId
  }))
});

console.log(`Sprint setup complete: ${selectedItems.length} items assigned`);

Sprint Rollover

Move incomplete tasks to next sprint:

// Get incomplete tasks from current sprint
const current = await callTool('list_work_items', {
  projectId: 230954,
  boardId: 650299  // Current sprint
});

const incomplete = current.items.filter(t => !t.isCompleted);

// Create next sprint
const nextSprint = await callTool('create_boards', {
  projectId: 230954,
  boards: [{
    name: 'Sprint 5: Continuation',
    startDate: '2026-01-15T00:00:00Z',
    endDate: '2026-01-28T23:59:59Z'
  }]
});

// Move incomplete items
await callTool('update_work_items', {
  projectId: 230954,
  items: incomplete.map(task => ({
    workItemId: task.workItemId,
    boardId: nextSprint.items[0].boardId
  }))
});

console.log(`Moved ${incomplete.length} incomplete tasks to next sprint`);

Daily Workflows

Daily Standup Report

Automated standup summary:

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

// Group by stage
const inProgress = myTasks.items.filter(t => t.stageName === 'In Progress');
const testing = myTasks.items.filter(t => t.stageName === 'Testing');
const review = myTasks.items.filter(t => t.stageName === 'Review');

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

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

// Generate report
console.log(`πŸ“Š Daily Standup - ${new Date().toLocaleDateString()}\n`);

console.log(`βœ… Yesterday:`);
// Show recently completed tasks
const yesterday = myTasks.items.filter(t =>
  t.isCompleted &&
  new Date(t.updatedAt) > new Date(Date.now() - 86400000)
);
yesterday.forEach(t => console.log(`   - ${t.title} (${t.estimatedCost}h)`));

console.log(`\nπŸ”„ Today (${inProgress.length + testing.length + review.length} tasks):`);
console.log(`   In Progress: ${inProgress.length}`);
inProgress.slice(0, 3).forEach(t => console.log(`      - ${t.title}`));
console.log(`   Testing: ${testing.length}`);
console.log(`   Review: ${review.length}`);

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

End of Day Workflow

Wrap up daily work:

// Get today's active tasks
const activeTasks = await callTool('get_my_current_tasks', {
  includeCompleted: false
});

const inProgress = activeTasks.items.filter(t => t.stageName === 'In Progress');

// Complete tasks in progress (logs time automatically)
for (const task of inProgress) {
  await callTool('complete_task', {
    workItemId: task.workItemId,
    comment: `End of day: ${new Date().toISOString().split('T')[0]}`
  });
  // Auto-logs hours if started with start_task
}

// Get summary
const summary = await callTool('get_my_current_tasks', {
  includeCompleted: true
});

const todayCompleted = summary.items.filter(t =>
  t.isCompleted &&
  new Date(t.updatedAt).toDateString() === new Date().toDateString()
);

const hoursLogged = todayCompleted.reduce((sum, t) =>
  sum + (t.loggedCost || 0), 0
);

console.log(`\nπŸ“ End of Day Summary:`);
console.log(`   Completed: ${todayCompleted.length} tasks`);
console.log(`   Hours logged: ${hoursLogged}h`);

Task Management

Complete Task Workflow

Full task lifecycle:

// 1. Start task (begins time tracking)
await callTool('start_task', {
  workItemId: 'Implement OAuth2 authentication',
  comment: 'Starting implementation. Using passport.js library.'
});

// 2. Add progress update
await callTool('add_comments', {
  comments: [{
    workItemId: 42,
    text: `## Progress Update

- [x] Set up passport.js
- [x] Configure GitHub strategy
- [ ] Add token refresh logic
- [ ] Write tests`
  }]
});

// ... work on task for 12 hours ...

// 3. Complete with automatic time logging
await callTool('complete_task', {
  workItemId: 42,
  comment: `## Completed

OAuth2 authentication fully implemented.

**Features:**
- GitHub login βœ…
- Google login βœ…
- Token refresh βœ…
- Logout βœ…

**Testing:**
All tests passing. Ready for code review.`
});
// βœ… Automatically logged 12h

Batch Task Updates

Move multiple tasks through workflow:

// Get tasks in "Testing" stage
const testing = await callTool('search_work_items', {
  projectId: 230954,
  stageId: 3,  // Testing
  isCompleted: false
});

// Get stage IDs
const stages = await callTool('list_stages', { projectId: 230954 });
const doneStageId = stages.items.find(s => s.name === 'Done').stageId;

// Move all tested tasks to Done
await callTool('update_work_items', {
  items: testing.items.map(task => ({
    workItemId: task.workItemId,
    stageId: doneStageId,
    isCompleted: true
  }))
});

console.log(`Moved ${testing.items.length} tasks to Done`);

Dependency Tracking

Define Task Dependencies

Create dependency chain:

const tasks = {
  schema: 100,    // Design database schema
  api: 101,       // Implement API endpoints
  ui: 102,        // Build UI components
  tests: 103      // Write tests
};

// Define dependency chain: schema β†’ API β†’ UI β†’ tests
await callTool('add_work_item_dependency', {
  successorId: tasks.api,
  predecessorId: tasks.schema
});

await callTool('add_work_item_dependency', {
  successorId: tasks.ui,
  predecessorId: tasks.api
});

await callTool('add_work_item_dependency', {
  successorId: tasks.tests,
  predecessorId: tasks.ui
});

// Check critical path
const blockers = await callTool('get_blockers', {
  projectId: 230954,
  workItemId: tasks.tests,
  includeIndirect: true  // Include full chain
});

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

Identify Bottlenecks

Find tasks blocking the most work:

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

// Count how many tasks each item blocks
const blockingCounts = {};
allBlockers.items.forEach(item => {
  item.blockedBy.forEach(blockerId => {
    blockingCounts[blockerId] = (blockingCounts[blockerId] || 0) + 1;
  });
});

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

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

Pagination

Handle Large Result Sets

Efficient pagination for large datasets:

// Get total count first (uses cache)
const initial = await callTool('list_work_items', {
  projectId: 230954,
  limit: 1  // Minimal first request
});

const totalItems = initial.total;
const pageSize = 50;
const totalPages = Math.ceil(totalItems / pageSize);

console.log(`Total items: ${totalItems} (${totalPages} pages)`);

// Fetch pages as needed
let allItems = [];
for (let page = 0; page < totalPages; page++) {
  const result = await callTool('list_work_items', {
    projectId: 230954,
    limit: pageSize,
    offset: page * pageSize
  });

  allItems = allItems.concat(result.items);

  if (!result.hasMore) break;
}

console.log(`Fetched ${allItems.length} total items`);

Use Slim Mode Index

Leverage slim mode quick-access index:

// Single request gets all IDs
const result = await callTool('list_work_items', {
  projectId: 230954,
  verbosity: 'slim',
  limit: 50
});

// Index contains ALL items (not just current page)
console.log(`Page items: ${result.items.length}`);
console.log(`Total items in index: ${result.index.length}`);

// Find specific task by partial title
const target = result.index.find(([id, title]) =>
  title.includes('OAuth')
);

if (target) {
  const [taskId, taskTitle] = target;
  console.log(`Found: #${taskId} - ${taskTitle}`);

  // Fetch full details only for this task
  const fullTask = await callTool('get_work_item', {
    workItemId: taskId
  });
}

See Pagination Limitations for API constraints.


See Also: