- name
- graph
- description
- 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".
- version
- 1.0
- generated_from
- arscontexta-v1.6
- user-invocable
- true
- context
- fork
- model
- sonnet
- allowed-tools
- Read, Grep, Glob, Bash
- argument-hint
- [operation] [target] — operations: health, triangles, bridges, clusters, hubs, siblings, forward, backward, query
## Runtime Configuration (Step 0 — before any processing)
Read these files to configure domain-specific behavior:
1. **`ops/derivation-manifest.md`** — vocabulary mapping, platform hints
- Use `vocabulary.notes` for the notes folder name
- Use `vocabulary.note` / `vocabulary.note_plural` for note type references
- Use `vocabulary.topic_map` / `vocabulary.topic_map_plural` for MOC references
- Use `vocabulary.cmd_reflect` for connection-finding command name
- Use `vocabulary.cmd_reweave` for backward-pass command name
2. **`ops/config.yaml`** — for graph thresholds (MOC size limits, orphan thresholds)
If no derivation file exists, use universal terms (notes, MOCs, etc.).
---
## EXECUTE NOW
**Target: $ARGUMENTS**
Parse the operation from arguments:
- If arguments match a known operation: route to that operation
- If arguments are a natural language question: map to the closest operation (see Interactive Mode)
- If no arguments: enter interactive mode
**START NOW.** Route to the appropriate operation.
---
## Philosophy
**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.
---
## Operations
### /graph health
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 metrics
- `ops/scripts/graph/orphan-notes.sh` for orphan detection
- `ops/scripts/graph/dangling-links.sh` for dangling link detection
**Step 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 |
### /graph triangles
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:
1. Check if B links to C (in either direction)
2. Check if C links to B (in either direction)
3. If neither link exists: this is an open triangle (synthesis opportunity)
**Step 3: Evaluate and rank**
For each open triangle:
1. Read descriptions of BOTH unlinked {vocabulary.note_plural}
2. Assess: is there a genuine conceptual relationship that the common parent suggests?
3. Rank by potential value: how surprising and useful would the connection be?
**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:
- Both are in the same {vocabulary.topic_map} (they may already be related through the MOC without direct links)
- One is a {vocabulary.topic_map} itself (MOCs link to everything, triangles with MOCs are noise)
- The descriptions suggest no conceptual overlap
### /graph bridges
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:
- Removing it (and its links) would split a connected component into two or more components
- It is the SOLE connection between clusters of {vocabulary.note_plural}
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."]
```
### /graph clusters
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:
1. Start with any unvisited note
2. Traverse all reachable notes via wiki links (bidirectional)
3. Mark as one component
4. Repeat until all notes visited
**Step 3: Analyze clusters**
For each cluster:
- Size (number of {vocabulary.note_plural})
- Key {vocabulary.note_plural} (highest link count within cluster)
- Topic coverage (which {vocabulary.topic_map_plural} are represented)
- Isolation level (how many links cross cluster boundaries)
**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."]
```
### /graph hubs
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."]
```
### /graph siblings [[topic]]
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}**
GitHubで見る