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
JavaScript
/**
* 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