UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

156 lines 5.91 kB
/** * Artifact Dependency Graph * * Traverses the artifact dependency graph built from @-mention relationships. * Shows upstream (what this depends on) and downstream (what depends on this). * * @implements #417 * @source @src/artifacts/types.ts * @tests @test/unit/artifacts/dep-graph.test.ts */ import { normalizeEdges } from './types.js'; import { loadDependencyGraph, loadGraphIndexFile } from './index-reader.js'; /** * Traverse the dependency graph in one direction * * @param edgeType - Optional filter: only follow edges of this type */ function traverse(graph, startPath, direction, maxDepth, visited = new Set(), currentDepth = 0, edgeType) { if (currentDepth >= maxDepth) return []; const node = graph[startPath]; if (!node) return []; const rawEdges = direction === 'upstream' ? node.upstream : node.downstream; // Normalize for backward compat (old indexes may have string arrays) const edges = normalizeEdges(rawEdges); // Filter by edge type if specified const filtered = edgeType ? edges.filter(e => e.type === edgeType) : edges; const results = []; for (const edge of filtered) { if (visited.has(edge.path)) continue; // Cycle detection visited.add(edge.path); const children = traverse(graph, edge.path, direction, maxDepth, visited, currentDepth + 1, edgeType); results.push({ path: edge.path, depth: currentDepth + 1, children }); } return results; } /** * Format tree output for human readability */ function formatTree(results, prefix = '') { let output = ''; for (let i = 0; i < results.length; i++) { const isLast = i === results.length - 1; const connector = isLast ? '└── ' : '├── '; const childPrefix = isLast ? ' ' : '│ '; output += `${prefix}${connector}${results[i].path}\n`; if (results[i].children.length > 0) { output += formatTree(results[i].children, prefix + childPrefix); } } return output; } /** * Flatten traversal results into a unique path list */ function flattenResults(results) { const paths = []; for (const r of results) { paths.push(r.path); paths.push(...flattenResults(r.children)); } return [...new Set(paths)]; } /** * Show dependencies for an artifact */ export async function showDeps(cwd, artifactPath, options = {}) { const { direction = 'both', depth = 3, json = false, graph: graphType, edgeType } = options; let depGraph = null; if (graphType) { depGraph = loadGraphIndexFile(cwd, 'dependencies.json', graphType); if (!depGraph) { console.error(`Error: No artifact index found for graph '${graphType}'.`); console.log("Run 'aiwg index build' first to create the index."); process.exit(1); } } else { // Merge dependency graphs from project-local graphs const graphTypes = ['project', 'codebase']; const merged = {}; for (const g of graphTypes) { const partial = loadGraphIndexFile(cwd, 'dependencies.json', g); if (partial) Object.assign(merged, partial); } if (Object.keys(merged).length > 0) { depGraph = merged; } else { // Legacy fallback depGraph = loadDependencyGraph(cwd); if (!depGraph) { console.error('Error: No artifact index found.'); console.log("Run 'aiwg index build' first to create the index."); process.exit(1); } } } if (!depGraph[artifactPath]) { console.error(`Error: '${artifactPath}' not found in the dependency index.`); console.log('Check the path or run `aiwg index build` to refresh.'); process.exit(1); } const showUpstream = direction === 'upstream' || direction === 'both'; const showDownstream = direction === 'downstream' || direction === 'both'; const upstreamResults = showUpstream ? traverse(depGraph, artifactPath, 'upstream', depth, new Set([artifactPath]), 0, edgeType) : []; const downstreamResults = showDownstream ? traverse(depGraph, artifactPath, 'downstream', depth, new Set([artifactPath]), 0, edgeType) : []; if (json) { console.log(JSON.stringify({ artifact: artifactPath, direction, depth, upstream: flattenResults(upstreamResults), downstream: flattenResults(downstreamResults), upstreamCount: flattenResults(upstreamResults).length, downstreamCount: flattenResults(downstreamResults).length, }, null, 2)); } else { console.log(`Dependencies for ${artifactPath}:`); console.log(''); if (showUpstream) { console.log(' UPSTREAM (this artifact depends on):'); if (upstreamResults.length === 0) { console.log(' (none)'); } else { const tree = formatTree(upstreamResults, ' '); process.stdout.write(tree); } console.log(''); } if (showDownstream) { console.log(' DOWNSTREAM (depends on this artifact):'); if (downstreamResults.length === 0) { console.log(' (none)'); } else { const tree = formatTree(downstreamResults, ' '); process.stdout.write(tree); } console.log(''); } const upCount = flattenResults(upstreamResults).length; const downCount = flattenResults(downstreamResults).length; console.log(` Upstream: ${upCount} | Downstream: ${downCount} | Total: ${upCount + downCount}`); } } //# sourceMappingURL=dep-graph.js.map