# Graph Query

> Query a graphify knowledge graph — traverse, explain nodes, find shortest paths

- Skill: `coin1860/graph-query` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add coin1860/graph-query`
- Raw SKILL.md: https://api.skillmd.com/api/skills/coin1860/graph-query/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: coin1860 (https://skillmd.com/u/coin1860)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/coin1860/graph-query

---


# /graph-query

Query a pre-built knowledge graph using BFS or DFS traversal, explain individual nodes, or find shortest paths between concepts.

> **Prerequisite:** A graph must already exist at `graphify-out/graph.json`. If it doesn't, run `/graph-build <path>` first to build it.

## Usage

```
/graph-query "<question>"          # BFS traversal — broad context
/graph-query "<question>" --dfs    # DFS — trace a specific path
/graph-query "<question>" --budget 1500   # cap answer at N tokens
/graph-query explain "<node name>"       # plain-language explanation of a node
/graph-query path "<NodeA>" "<NodeB>"    # shortest path between two concepts
```

---

## For /graph-query (BFS/DFS traversal)

Two traversal modes — choose based on the question:

| Mode | Flag | Best for |
|------|------|----------|
| BFS (default) | _(none)_ | "What is X connected to?" - broad context, nearest neighbors first |
| DFS | `--dfs` | "How does X reach Y?" - trace a specific chain or dependency path |

First check the graph exists:

```python
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
    print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
    raise SystemExit(1)
"
```

If it fails, stop and tell the user to run `/graphify <path>` first.

Load `graphify-out/graph.json`, then:

1. Find the 1-3 nodes whose label best matches key terms in the question.
2. Run the appropriate traversal from each starting node.
3. Read the subgraph — node labels, edge relations, confidence tags, source locations.
4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact.
5. If the graph lacks enough information, say so — do not hallucinate edges.

```python
python -c "
import sys, json
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path

data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)

question = 'QUESTION'
mode = 'MODE'  # 'bfs' or 'dfs'
terms = [t.lower() for t in question.split() if len(t) > 3]

# Find best-matching start nodes
scored = []
for nid, ndata in G.nodes(data=True):
    label = ndata.get('label', '').lower()
    score = sum(1 for t in terms if t in label)
    if score > 0:
        scored.append((score, nid))
scored.sort(reverse=True)
start_nodes = [nid for _, nid in scored[:3]]

if not start_nodes:
    print('No matching nodes found for query terms:', terms)
    sys.exit(0)

subgraph_nodes = set()
subgraph_edges = []

if mode == 'dfs':
    # DFS: follow one path as deep as possible before backtracking.
    # Depth-limited to 6 to avoid traversing the whole graph.
    visited = set()
    stack = [(n, 0) for n in reversed(start_nodes)]
    while stack:
        node, depth = stack.pop()
        if node in visited or depth > 6:
            continue
        visited.add(node)
        subgraph_nodes.add(node)
        for neighbor in G.neighbors(node):
            if neighbor not in visited:
                stack.append((neighbor, depth + 1))
                subgraph_edges.append((node, neighbor))
else:
    # BFS: explore all neighbors layer by layer up to depth 3.
    frontier = set(start_nodes)
    subgraph_nodes = set(start_nodes)
    for _ in range(3):
        next_frontier = set()
        for n in frontier:
            for neighbor in G.neighbors(n):
                if neighbor not in subgraph_nodes:
                    next_frontier.add(neighbor)
                    subgraph_edges.append((n, neighbor))
        subgraph_nodes.update(next_frontier)
        frontier = next_frontier

# Token-budget aware output: rank by relevance, cut at budget (~4 chars/token)
token_budget = 2000  # default; override with --budget N
char_budget = token_budget * 4

def relevance(nid):
    label = G.nodes[nid].get('label', '').lower()
    return sum(1 for t in terms if t in label)

ranked_nodes = sorted(subgraph_nodes, key=relevance, reverse=True)

lines = [f'Traversal: {mode.upper()} | Start: {[G.nodes[n].get(\"label\",n) for n in start_nodes]} | {len(subgraph_nodes)} nodes']
for nid in ranked_nodes:
    d = G.nodes[nid]
    lines.append(f'  NODE {d.get(\"label\", nid)} [src={d.get(\"source_file\",\"\")} loc={d.get(\"source_location\",\"\")}]')
for u, v in subgraph_edges:
    if u in subgraph_nodes and v in subgraph_nodes:
        d = G.edges[u, v]
        lines.append(f'  EDGE {G.nodes[u].get(\"label\",u)} --{d.get(\"relation\",\"\")} [{d.get(\"confidence\",\"\")}]--> {G.nodes[v].get(\"label\",v)}')

output = '\n'.join(lines)
if len(output) > char_budget:
    output = output[:char_budget] + f'\n... (truncated at ~{token_budget} token budget - use --budget N for more)'
print(output)
"
```

Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above.

After writing the answer, save it back into the graph so it improves future queries:

```python
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'QUESTION', '--answer', 'ANSWER', '--type', 'query', '--nodes', 'NODE1', 'NODE2'])"
```

Each answer should end with a natural follow-up ("this connects to X — want to go deeper?") so the session feels like navigation, not a one-shot report.

---

## For /graphify explain

Give a plain-language explanation of a single node — everything connected to it.

First check the graph exists:

```python
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
    print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
    raise SystemExit(1)
"
```

```python
python -c "
import json, sys
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path

data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)

term = 'NODE_NAME'
term_lower = term.lower()

# Find best matching node
scored = sorted(
    [(sum(1 for w in term_lower.split() if w in G.nodes[n].get('label','').lower()), n)
     for n in G.nodes()],
    reverse=True
)
if not scored or scored[0][0] == 0:
    print(f'No node matching {term!r}')
    sys.exit(0)

nid = scored[0][1]
data_n = G.nodes[nid]
print(f'NODE: {data_n.get(\"label\", nid)}')
print(f'  source: {data_n.get(\"source_file\",\"unknown\")}')
print(f'  type: {data_n.get(\"file_type\",\"unknown\")}')
print(f'  degree: {G.degree(nid)}')
print()
print('CONNECTIONS:')
for neighbor in G.neighbors(nid):
    edge = G.edges[nid, neighbor]
    nlabel = G.nodes[neighbor].get('label', neighbor)
    rel = edge.get('relation', '')
    conf = edge.get('confidence', '')
    src_file = G.nodes[neighbor].get('source_file', '')
    print(f'  --{rel}--> {nlabel} [{conf}] ({src_file})')
"
```

Replace `NODE_NAME` with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations.

After writing the explanation, save it back:

```python
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'Explain NODE_NAME', '--answer', 'ANSWER', '--type', 'explain', '--nodes', 'NODE_NAME'])"
```

---

## For /graphify path

Find the shortest path between two named concepts in the graph.

First check the graph exists:

```python
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
    print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
    raise SystemExit(1)
"
```

```python
python -c "
import json, sys
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path

data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)

a_term = 'NODE_A'
b_term = 'NODE_B'

def find_node(term):
    term = term.lower()
    scored = sorted(
        [(sum(1 for w in term.split() if w in G.nodes[n].get('label','').lower()), n)
         for n in G.nodes()],
        reverse=True
    )
    return scored[0][1] if scored and scored[0][0] > 0 else None

src = find_node(a_term)
tgt = find_node(b_term)

if not src or not tgt:
    print(f'Could not find nodes matching: {a_term!r} or {b_term!r}')
    sys.exit(0)

try:
    path = nx.shortest_path(G, src, tgt)
    print(f'Shortest path ({len(path)-1} hops):')
    for i, nid in enumerate(path):
        label = G.nodes[nid].get('label', nid)
        if i < len(path) - 1:
            edge = G.edges[nid, path[i+1]]
            rel = edge.get('relation', '')
            conf = edge.get('confidence', '')
            print(f'  {label} --{rel}--> [{conf}]')
        else:
            print(f'  {label}')
except nx.NetworkXNoPath:
    print(f'No path found between {a_term!r} and {b_term!r}')
except nx.NodeNotFound as e:
    print(f'Node not found: {e}')
"
```

Replace `NODE_A` and `NODE_B` with the actual concept names from the user. Then explain the path in plain language — what each hop means, why it's significant.

After writing the explanation, save it back:

```python
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'Path from NODE_A to NODE_B', '--answer', 'ANSWER', '--type', 'path_query', '--nodes', 'NODE_A', 'NODE_B'])"
```

---

## Honesty Rules

- Never invent an edge. If unsure, use AMBIGUOUS.
- Answer using **only** what the graph contains — do not hallucinate connections.
- Always cite `source_location` when referencing a specific fact.
- If the graph lacks enough information, say so explicitly.

