Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Interactive knowledge graph analysis. Routes natural language questions to graph scripts, interprets results in domain vocabulary, and suggests concrete actions. Triggers on "/graph", "/graph health", "/graph triangles", "find synthesis opportunities", "graph analysis".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 156% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 136% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 206% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 171% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 279% | 0% |
Read these files to configure domain-specific behavior:
ops/derivation-manifest.md — vocabulary mapping, platform hintsvocabulary.notes for the notes folder namevocabulary.note / vocabulary.note_plural for note type referencesvocabulary.topic_map / vocabulary.topic_map_plural for MOC referencesvocabulary.cmd_reflect for connection-finding command namevocabulary.cmd_reweave for backward-pass command nameops/config.yaml — for graph thresholds (MOC size limits, orphan thresholds)If no derivation file exists, use universal terms (notes, MOCs, etc.).
Target: $ARGUMENTS
Parse the operation from arguments:
START NOW. Route to the appropriate operation.
The graph IS the knowledge. This skill makes it visible.
Individual {vocabulary.note_plural} are valuable, but their connections create compound value. /graph reveals the structural properties of those connections — where the graph is dense, where it is sparse, where it is fragile, and where synthesis opportunities hide.
Every operation produces two things: findings (what the analysis reveals) and actions (what to do about it). Never dump raw data. Always interpret results with {vocabulary.note} descriptions and domain context. Always suggest specific next steps.
Full graph health report: density, orphans, dangling links, coverage.
Step 1: Collect raw metrics
bash# Count total notes (excluding MOCs) NOTES_DIR="{vocabulary.notes}" TOTAL=$(ls -1 "$NOTES_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ') MOC_COUNT=$(grep -rl '^type: moc' "$NOTES_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ') NOTE_COUNT=$((TOTAL - MOC_COUNT)) # Count all wiki links LINK_COUNT=$(grep -ohP '\[\[[^\]]+\]\]' "$NOTES_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ') # Calculate link density # Density = actual_links / possible_links # possible_links = N * (N - 1) for directed graph echo "Density: $LINK_COUNT / ($NOTE_COUNT * ($NOTE_COUNT - 1))" # Find orphan notes (zero incoming links) for f in "$NOTES_DIR"/*.md; do NAME=$(basename "$f" .md) INCOMING=$(grep -rl "\[\[$NAME\]\]" "$NOTES_DIR"/ 2>/dev/null | grep -v "$f" | wc -l | tr -d ' ') [[ "$INCOMING" -eq 0 ]] && echo "ORPHAN: $NAME" done # Find dangling links (links to non-existent files) grep -ohP '\[\[([^\]]+)\]\]' "$NOTES_DIR"/*.md 2>/dev/null | sort -u | while read -r link; do NAME=$(echo "$link" | sed 's/\[\[//;s/\]\]//') [[ ! -f "$NOTES_DIR/$NAME.md" ]] && echo "DANGLING: $NAME" done # MOC coverage: % of notes appearing in at least one MOC's Core Ideas COVERED=0 for f in "$NOTES_DIR"/*.md; do NAME=$(basename "$f" .md) # Skip MOCs themselves grep -q '^type: moc' "$f" 2>/dev/null && continue # Check if any MOC links to this note if grep -rl '^type: moc' "$NOTES_DIR"/*.md 2>/dev/null | xargs grep -l "\[\[$NAME\]\]" >/dev/null 2>&1; then COVERED=$((COVERED + 1)) fi done echo "Coverage: $COVERED / $NOTE_COUNT"
If graph helper scripts exist in ops/scripts/graph/, use them instead of inline analysis:
ops/scripts/graph/link-density.sh for density metricsops/scripts/graph/orphan-notes.sh for orphan detectionops/scripts/graph/dangling-links.sh for dangling link detectionStep 2: Interpret and present
--=={ graph health }==--
{vocabulary.note_plural}: [N] (plus [M] {vocabulary.topic_map_plural})
Connections: [N] (avg [X] per {vocabulary.note})
Graph density: [0.XX]
{vocabulary.topic_map} coverage: [N]% of {vocabulary.note_plural} appear in at least one {vocabulary.topic_map}
Orphans ([N]):
- [[orphan name]] — [description from YAML]
→ Suggestion: Run /{vocabulary.cmd_reflect} to find connections
Dangling Links ([N]):
- [[missing name]] — referenced from [[source note]]
→ Suggestion: Create the {vocabulary.note} or remove the link
{vocabulary.topic_map} Sizes:
- [[moc name]]: [N] {vocabulary.note_plural} [OK | WARN: approaching split threshold | WARN: consider merging]
Overall: [HEALTHY | NEEDS ATTENTION | FRAGMENTED]Density benchmarks:
| Density | Interpretation | |---------|---------------| | < 0.02 | Sparse — {vocabulary.note_plural} exist but connections are thin | | 0.02-0.06 | Healthy — growing network with meaningful connections | | 0.06-0.15 | Dense — well-connected, watch for over-linking | | > 0.15 | Very dense — verify connections are genuine, not noise |
Find synthesis opportunities — open triadic closures where A links to B and A links to C, but B does not link to C.
Step 1: Build adjacency data
bash# For each note, extract outgoing wiki links for f in "$NOTES_DIR"/*.md; do NAME=$(basename "$f" .md) LINKS=$(grep -oP '\[\[([^\]]+)\]\]' "$f" 2>/dev/null | sed 's/\[\[//;s/\]\]//' | sort -u) echo "FROM:$NAME" echo "$LINKS" | while read -r target; do [[ -n "$target" ]] && echo " TO:$target" done done
If ops/scripts/graph/find-triangles.sh exists, use it directly.
Step 2: Find open triangles
For each note A with outgoing links to B and C:
Step 3: Evaluate and rank
For each open triangle:
Step 4: Present top findings
--=={ graph triangles }==--
Found [N] synthesis opportunities — pairs of {vocabulary.note_plural} that share
a common reference but do not reference each other:
1. [[note B]] and [[note C]]
Common parent: [[note A]]
B: "[description]"
C: "[description]"
→ These may benefit from a connection because [specific reasoning
about WHY B and C might relate through A's lens]
→ Action: Run /{vocabulary.cmd_reflect} on [[note B]] to evaluate
2. [[note D]] and [[note E]]
Common parent: [[note F]]
...
[Show top 10. If more exist: "[N] more triangles found. Show all? (yes/no)"]Filter out trivial triangles: Skip pairs where:
Identify structurally critical {vocabulary.note_plural} whose removal would disconnect graph regions.
Step 1: Build adjacency list
Build a bidirectional adjacency list from all wiki links in {vocabulary.notes}/.
If ops/scripts/graph/find-bridges.sh exists, use it directly.
Step 2: Find bridge nodes
A bridge note is one where:
Implementation: For each note, temporarily remove it and check if the remaining graph has more connected components.
Step 3: Present findings
--=={ graph bridges }==--
Found [N] bridge {vocabulary.note_plural} — structurally critical nodes whose
removal would disconnect graph regions:
1. [[bridge note]] — connects [N] {vocabulary.note_plural} on one side to [M] on the other
Description: "[description]"
Cluster A: [[note1]], [[note2]], ...
Cluster B: [[note3]], [[note4]], ...
→ Risk: If this {vocabulary.note} becomes stale, [N+M] {vocabulary.note_plural}
lose their connection path
→ Action: Consider adding parallel connections between the clusters
[If no bridges: "No bridge notes found. The graph has redundant paths between
all connected regions. This is healthy."]Discover connected components and topic boundaries.
Step 1: Build adjacency list
Build a bidirectional adjacency list from all wiki links.
If ops/scripts/graph/find-clusters.sh exists, use it directly.
Step 2: Find connected components
Use BFS/DFS to find all connected components:
Step 3: Analyze clusters
For each cluster:
Step 4: Present findings
--=={ graph clusters }==--
Found [N] connected components:
Cluster 1: [size] {vocabulary.note_plural}
Key nodes: [[note1]] (8 links), [[note2]] (6 links)
Topics: [[topic A]], [[topic B]]
Cross-cluster links: [N]
→ This cluster is [well-connected | isolated | a hub]
Cluster 2: [size] {vocabulary.note_plural}
...
Isolated {vocabulary.note_plural} ([N]):
- [[isolated note]] — [description]
→ Action: Run /{vocabulary.cmd_reflect} to find connections
[If 1 cluster: "All {vocabulary.note_plural} are in one connected component.
The graph is fully connected. This is healthy."]Rank {vocabulary.note_plural} by influence — most-linked-to (authorities) and most-linking-from (hubs).
Step 1: Count links
bash# Authority score: incoming links per note for f in "$NOTES_DIR"/*.md; do NAME=$(basename "$f" .md) INCOMING=$(grep -rl "\[\[$NAME\]\]" "$NOTES_DIR"/ 2>/dev/null | grep -v "$f" | wc -l | tr -d ' ') echo "AUTH:$INCOMING:$NAME" done | sort -t: -k2 -rn | head -10 # Hub score: outgoing links per note for f in "$NOTES_DIR"/*.md; do NAME=$(basename "$f" .md) OUTGOING=$(grep -oP '\[\[[^\]]+\]\]' "$f" 2>/dev/null | wc -l | tr -d ' ') echo "HUB:$OUTGOING:$NAME" done | sort -t: -k2 -rn | head -10
If ops/scripts/graph/influence-flow.sh exists, use it directly.
Step 2: Identify synthesizers
Synthesizer {vocabulary.note_plural} score high on BOTH metrics — they absorb many inputs (high authority) and produce many outputs (high hub). These are the most structurally important {vocabulary.note_plural} in the graph.
Step 3: Present findings
--=={ graph hubs }==--
Top Authorities (most-linked-to):
1. [[note]] — [N] incoming links — "[description]"
2. [[note]] — [N] incoming links — "[description]"
...
Top Hubs (most-linking-from):
1. [[note]] — [N] outgoing links — "[description]"
2. [[note]] — [N] outgoing links — "[description]"
...
Synthesizers (high on both — structurally important):
1. [[note]] — [N] in / [M] out — "[description]"
...
[If no clear synthesizers: "No notes score high on both metrics.
This suggests the graph has separate input and output layers."]Find unconnected {vocabulary.note_plural} within a topic — {vocabulary.note_plural} sharing the same {vocabulary.topic_map} but not linking to each other.
Step 1: Read the specified {vocabulary.topic_map}
Find and read the {vocabulary.topic_map} matching the argument. Extract all {vocabulary.note_plural} linked in Core Ideas.
Step 2: Check pairwise connections
For each pair of {vocabulary.note_plural} in the {vocabulary.topic_map}:
[[B]] in A's file)[[A]] in B's file)If ops/scripts/graph/topic-siblings.sh exists, use it with the topic argument.
Step 3: Evaluate pairs
For each unconnected pair:
Step 4: Present findings
--=={ graph siblings: [[topic]] }==--
{vocabulary.topic_map} [[topic]] has [N] {vocabulary.note_plural}.
Found [M] unconnected sibling pairs:
Likely connections:
1. [[note A]] and [[note B]]
A: "[description]"
B: "[description]"
→ [Why these likely relate]
Possible connections:
2. [[note C]] and [[note D]]
...
Appropriately separate: [N] pairs — no connection needed
→ Action: Run /{vocabulary.cmd_reflect} on the "likely" pairsN-hop forward traversal from a {vocabulary.note}. Default depth: 2.
Step 1: Start from the specified {vocabulary.note}
Read the {vocabulary.note} and extract all outgoing wiki links (hop 1).
If ops/scripts/graph/n-hop-forward.sh exists, use it with the note and depth arguments.
Step 2: Traverse
For each linked {vocabulary.note}:
Step 3: Present as annotated tree
--=={ forward traversal: [[note]] (depth [N]) }==--
[[root note]] — "[description]"
├── [[link 1]] — "[description]"
│ ├── [[link 1a]] — "[description]"
│ └── [[link 1b]] — "[description]"
├── [[link 2]] — "[description]"
│ └── [[link 2a]] — "[description]"
└── [[link 3]] — "[description]"
Reached [N] {vocabulary.note_plural} in [depth] hops.
Dead ends (no outgoing links): [[note X]], [[note Y]]
Cycles detected: [[note]] → ... → [[note]] (skipped)N-hop backward traversal to a {vocabulary.note}. Default depth: 2.
Step 1: Start from the specified {vocabulary.note}
Find all notes that link TO this {vocabulary.note} (hop 1).
bashNAME="[note name]" grep -rl "\[\[$NAME\]\]" "$NOTES_DIR"/*.md 2>/dev/null
If ops/scripts/graph/recursive-backlinks.sh exists, use it with the note and depth arguments.
Step 2: Traverse backward
For each linking {vocabulary.note}:
Step 3: Present as annotated tree
--=={ backward traversal: [[note]] (depth [N]) }==--
[[root note]] — "[description]"
├── [[referrer 1]] — "[description]"
│ ├── [[referrer 1a]] — "[description]"
│ └── [[referrer 1b]] — "[description]"
├── [[referrer 2]] — "[description]"
│ └── [[referrer 2a]] — "[description]"
└── [[referrer 3]] — "[description]"
[N] {vocabulary.note_plural} lead to [[root note]] within [depth] hops.
Entry points (no incoming links): [[note X]], [[note Y]]Schema-level YAML query across {vocabulary.note_plural}.
Step 1: Parse field and value
Supported query patterns:
| Query | Ripgrep Pattern | Purpose | |-------|----------------|---------| | topics [[X]] | rg '^topics:.*\[\[X\]\]' | Find notes in a topic | | type tension | rg '^type: tension' | Find notes by type | | methodology X | rg '^methodology:.*X' | Find notes by tradition | | status open | rg '^status: open' | Find notes by status | | created 2026-02 | rg '^created: 2026-02' | Find notes by date range | | source [[X]] | rg '^source:.*\[\[X\]\]' | Find notes from a source |
Step 2: Execute query
bashrg "^{field}:.*{value}" "$NOTES_DIR"/*.md -l 2>/dev/null
For each matching file, extract the description for context.
Step 3: Present results
--=={ graph query: {field} = {value} }==--
Found [N] {vocabulary.note_plural}:
1. [[note name]] — "[description]"
2. [[note name]] — "[description]"
...
Distribution:
[If querying topics: how many per sub-topic]
[If querying type: breakdown by status]
[If querying methodology: breakdown by tradition]If no arguments provided:
| User Says | Maps To | Why | |-----------|---------|-----| | "Where should I look for connections?" | triangles | Finding synthesis opportunities | | "What are my most important notes?" | hubs | Authority/hub ranking | | "Are there isolated areas?" | clusters | Connected component detection | | "How healthy is my graph?" | health | Full health report | | "What bridges my topics?" | bridges | Bridge note identification | | "What connects to X]]?" | backward X]] | Backward traversal | | "Where does X]] lead?" | forward X]] | Forward traversal | | "Show me notes about topic]" | query topics topic]] | Schema query | | "What needs connecting in topic]?" | siblings topic]] | Unconnected sibling pairs |
Report metrics but contextualize: "With N] {vocabulary.note_plural}, graph analysis provides limited insight. Graph operations become more valuable as the knowledge graph grows. Current metrics are baseline measurements."
All operations still run — they just produce less data.
If ops/scripts/graph/ does not exist or individual scripts are missing, implement the analysis inline using grep, file reads, and bash loops as shown in each operation's steps. The inline implementations are complete — scripts are optimization, not requirements.
Use universal vocabulary (notes, MOCs, etc.). All operations work identically.
Report: "No {vocabulary.note_plural} found in {vocabulary.notes}/. Start by capturing content to build your knowledge graph."
If the specified {vocabulary.note} or {vocabulary.topic_map} does not exist:
ls "$NOTES_DIR"/*{query}*.md 2>/dev/nullOther measured skills in the registry, with their headline benchmark lift.