Map and analyze project dependencies
Maps and analyzes project dependencies to identify blockers and generate optimal task execution order.
/plugin marketplace add davepoon/buildwithclaude/plugin install all-commands@buildwithclaudeMap and analyze project dependencies
This command analyzes code dependencies, git history, and Linear tasks to create visual dependency maps. It helps identify blockers, circular dependencies, and optimal task ordering for efficient project execution.
# Map dependencies for a specific Linear task
claude "Show dependency map for task LIN-123"
# Analyze code dependencies in a module
claude "Map dependencies for src/auth module"
# Find circular dependencies in the project
claude "Check for circular dependencies in the codebase"
# Generate task execution order
claude "What's the optimal order to complete tasks in sprint SPR-45?"
Use various techniques to identify dependencies:
# Find import statements (JavaScript/TypeScript)
rg "^import.*from ['\"](\.\.?/[^'\"]+)" --type ts --type js -o | sort | uniq
# Find require statements (Node.js)
rg "require\(['\"](\.\.?/[^'\"]+)['\"]" --type js -o
# Analyze Python imports
rg "^from \S+ import|^import \S+" --type py
# Find module references in comments
rg "TODO.*depends on|FIXME.*requires|NOTE.*needs" -i
Query Linear for task relationships:
// Get task with its dependencies
const task = await linear.getTask(taskId, {
include: ['blockedBy', 'blocks', 'parent', 'children']
});
// Find mentions in task descriptions
const mentions = task.description.match(/(?:LIN-|#)\d+/g);
// Get related tasks from same epic/project
const relatedTasks = await linear.searchTasks({
projectId: task.projectId,
includeArchived: false
});
Create a graph structure:
class DependencyGraph {
constructor() {
this.nodes = new Map(); // taskId -> task details
this.edges = new Map(); // taskId -> Set of dependent taskIds
}
addDependency(from, to, type = 'blocks') {
if (!this.edges.has(from)) {
this.edges.set(from, new Set());
}
this.edges.get(from).add({ to, type });
}
findCycles() {
const visited = new Set();
const recursionStack = new Set();
const cycles = [];
const hasCycle = (node, path = []) => {
visited.add(node);
recursionStack.add(node);
path.push(node);
const neighbors = this.edges.get(node) || new Set();
for (const { to } of neighbors) {
if (!visited.has(to)) {
if (hasCycle(to, [...path])) return true;
} else if (recursionStack.has(to)) {
// Found cycle
const cycleStart = path.indexOf(to);
cycles.push(path.slice(cycleStart));
}
}
recursionStack.delete(node);
return false;
};
for (const node of this.nodes.keys()) {
if (!visited.has(node)) {
hasCycle(node);
}
}
return cycles;
}
topologicalSort() {
const inDegree = new Map();
const queue = [];
const result = [];
// Calculate in-degrees
for (const [node] of this.nodes) {
inDegree.set(node, 0);
}
for (const [_, edges] of this.edges) {
for (const { to } of edges) {
inDegree.set(to, (inDegree.get(to) || 0) + 1);
}
}
// Find nodes with no dependencies
for (const [node, degree] of inDegree) {
if (degree === 0) queue.push(node);
}
// Process queue
while (queue.length > 0) {
const node = queue.shift();
result.push(node);
const edges = this.edges.get(node) || new Set();
for (const { to } of edges) {
inDegree.set(to, inDegree.get(to) - 1);
if (inDegree.get(to) === 0) {
queue.push(to);
}
}
}
return result;
}
}
LIN-123: Authentication System
├─ LIN-124: User Model [DONE]
├─ LIN-125: JWT Implementation [IN PROGRESS]
│ └─ LIN-126: Token Refresh Logic [BLOCKED]
└─ LIN-127: Login Endpoint [TODO]
├─ LIN-128: Rate Limiting [TODO]
└─ LIN-129: 2FA Support [TODO]
graph TD
LIN-123[Authentication System] --> LIN-124[User Model]
LIN-123 --> LIN-125[JWT Implementation]
LIN-123 --> LIN-127[Login Endpoint]
LIN-125 --> LIN-126[Token Refresh Logic]
LIN-127 --> LIN-128[Rate Limiting]
LIN-127 --> LIN-129[2FA Support]
style LIN-124 fill:#90EE90
style LIN-125 fill:#FFD700
style LIN-126 fill:#FF6B6B
| LIN-123 | LIN-124 | LIN-125 | LIN-126 | LIN-127 |
---------|---------|---------|---------|---------|---------|
LIN-123 | - | → | → | | → |
LIN-124 | | - | | | |
LIN-125 | | ← | - | → | |
LIN-126 | | | ← | - | |
LIN-127 | ← | ← | | | - |
Legend: → depends on, ← is dependency of
Map code structure to tasks:
// Analyze file imports
async function analyzeFileDependencies(filePath) {
const content = await readFile(filePath);
const imports = extractImports(content);
const dependencies = {
internal: [], // Project files
external: [], // npm packages
tasks: [] // Related Linear tasks
};
for (const imp of imports) {
if (imp.startsWith('.')) {
dependencies.internal.push(resolveImportPath(filePath, imp));
} else {
dependencies.external.push(imp);
}
// Check if file is mentioned in any task
const tasks = await linear.searchTasks(path.basename(filePath));
dependencies.tasks.push(...tasks);
}
return dependencies;
}
Calculate optimal task sequence:
function calculateExecutionOrder(graph) {
const order = graph.topologicalSort();
const taskDetails = [];
for (const taskId of order) {
const task = graph.nodes.get(taskId);
const dependencies = Array.from(graph.edges.get(taskId) || [])
.map(({ to }) => to);
taskDetails.push({
id: taskId,
title: task.title,
estimate: task.estimate || 0,
dependencies,
assignee: task.assignee,
criticalPath: isOnCriticalPath(taskId, graph)
});
}
return taskDetails;
}
// Check for Linear access
if (!linear.available) {
console.warn("Linear MCP not available, using code analysis only");
// Fall back to code-only analysis
}
// Handle circular dependencies
const cycles = graph.findCycles();
if (cycles.length > 0) {
console.error("Circular dependencies detected:");
cycles.forEach(cycle => {
console.error(` ${cycle.join(' → ')} → ${cycle[0]}`);
});
}
// Validate task existence
for (const taskId of mentionedTasks) {
try {
await linear.getTask(taskId);
} catch (error) {
console.warn(`Task ${taskId} not found or inaccessible`);
}
}
Analyzing dependencies for Epic: Authentication System (LIN-123)
📊 Dependency Graph:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LIN-123: Authentication System [EPIC]
├─ LIN-124: Create User Model ✅ [DONE]
│ └─ Files: src/models/User.ts, src/schemas/user.sql
├─ LIN-125: Implement JWT Service 🚧 [IN PROGRESS]
│ ├─ Files: src/services/auth/jwt.ts
│ ├─ Depends on: LIN-124
│ └─ LIN-126: Add Token Refresh ⛔ [BLOCKED by LIN-125]
└─ LIN-127: Create Login Endpoint 📋 [TODO]
├─ Files: src/routes/auth/login.ts
├─ Depends on: LIN-124, LIN-125
├─ LIN-128: Add Rate Limiting 📋 [TODO]
└─ LIN-129: Implement 2FA 📋 [TODO]
🔄 Circular Dependencies: None found
📈 Critical Path:
1. LIN-124 (User Model) - 2 points ✅
2. LIN-125 (JWT Service) - 3 points 🚧
3. LIN-126 (Token Refresh) - 1 point ⛔
4. LIN-127 (Login Endpoint) - 2 points 📋
Total: 8 points on critical path
👥 Task Distribution:
- Alice: LIN-125 (in progress), LIN-126 (blocked)
- Bob: LIN-127 (ready to start)
- Unassigned: LIN-128, LIN-129
📁 File Dependencies:
src/routes/auth/login.ts
└─ imports from:
├─ src/models/User.ts (LIN-124) ✅
├─ src/services/auth/jwt.ts (LIN-125) 🚧
└─ src/middleware/rateLimiter.ts (LIN-128) 📋
⚡ Recommended Action:
Priority should be completing LIN-125 to unblock 3 dependent tasks.
Bob can start on LIN-124 prerequisite work while waiting.
Show what tasks are affected by changes:
# What tasks are impacted if we change User.ts?
claude "Show impact analysis for changes to src/models/User.ts"
Optimize task order for sprint capacity:
# Generate sprint plan considering dependencies
claude "Plan sprint with 20 points capacity considering dependencies"
Identify high-risk dependency chains:
# Find longest dependency chains
claude "Show tasks with longest dependency chains in current sprint"