Real-world recipes and workflows for common HacknPlan tasks.
- Feature Implementation
- Sprint Management
- Daily Workflows
- Task Management
- Dependency Tracking
- Pagination
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)
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`);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`);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(', ')})`)
);
}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`);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 12hMove 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`);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}`);
});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`);
});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`);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:
- Workflow Shortcuts - start_task, complete_task, create_subtask
- Batch Operations - search_work_items, get_blockers
- Best Practices - Optimization tips
- Error Handling - Handling failures