agentsclimarketplace

Obsidian graph

Skill lexbritvin/obsidian-skills-pack/skills/obsidian-graph

Agent Skills for Obsidian — plugin syntax (queries, tasks, charts, interactive UI), graph view tuning, vault auditing, heatmap visualizations. Compatible with Claude Code, Codex CLI, OpenCode, and any agent-skills runtime.

Install
npx -y skills add lexbritvin/obsidian-skills-pack --skill obsidian-graph

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Configure Obsidian Graph view — color groups, filters, forces, display settings, palette choice. Use whenever the user wants to change how the graph looks or what it shows, including coloring by folder/category/topic/tag, hiding noise via filter, adjusting forces (repel/link distance), or picking a palette. Triggers include "configure the graph", "color the graph", "fix the graph", "show/hide tags on the graph", "exclude folder from graph", "graph.json", "color groups", "graph filter", "graph forces", "graph palette", "make graph readable", "spread the graph out", "highlight hubs on the graph", "why is the graph one color".

SKILL.md

22.0 KB, as published. Nobody here has run it

Obsidian Graph view

Global graph configuration lives in .obsidian/graph.json. Local graph (Graph view opened from a single note) lives in .obsidian/workspace.json and does not inherit from global. This skill covers global graph unless explicitly noted.

For diagnosing what's wrong with a graph (over-connection, render-only edges, false categories), use the paired obsidian-graph-audit skill first.

Workflow when the user asks "configure my graph"

  1. Ask or read intent first. What's bothering them, or what do they want to see? Color groups without a goal are pure aesthetics — skip them.
  2. ⚠️ Have the user close Graph view (or quit Obsidian). This is mandatory — Obsidian rewrites graph.json when Graph view closes, wiping external edits. See Caveats.
  3. Read the current .obsidian/graph.json. Don't reset settings the user has tuned (especially scale).
  4. Measure vault size: find . -name '*.md' -not -path './.obsidian/*' -not -path './.trash/*' -not -path './.claude/*' | wc -l. Forces depend on this.
  5. Apply minimum changes, explain each. 14 color groups on 600 notes is too many — 3-8 is the proven range.
  6. Keep only what adds signal. Filter > group when you don't want to see something; group > filter when you want to differentiate.
  7. Verify decimal RGB math. A wrong byte in a color value silently renders as a different color. Always round-trip HEX↔decimal (see below).
  8. Have the user open Graph view after edits. Changes take effect on next open.

Full schema of graph.json

20 fields. Source: community gist on syncing graph settings, example repo. Obsidian has no official schema document.

Filters block

FieldTypePurpose
collapse-filterboolWhether the Filters block is collapsed in the UI
searchstringFilter query (same syntax as core Search)
showTagsboolTag nodes shown as separate nodes
showAttachmentsboolFiles (PNG, PDF) shown as nodes
hideUnresolvedboolHide unresolved wikilinks
showOrphansboolShow nodes with no connections

Color groups block

FieldTypePurpose
collapse-color-groupsboolWhether the Groups block is collapsed
colorGroupsarrayArray of {query, color} objects

Display block

FieldTypePurpose
collapse-displayboolWhether the Display block is collapsed
showArrowboolShow direction arrows on edges
textFadeMultipliernumber0 = labels never visible; 1 = always; ~0.5 = visible at moderate zoom
nodeSizeMultipliernumber1 = default; >1 makes hubs bigger
lineSizeMultipliernumber1 = default; <1 makes edges thinner

Forces block

FieldTypePurpose
collapse-forcesboolWhether the Forces block is collapsed
centerStrengthnumberPull toward center; 0.5 = sane default
repelStrengthnumberNode repulsion; 10 default, 15-25 for larger vaults
linkStrengthnumberEdge spring stiffness; 0.7-1
linkDistancenumberTarget edge length; 150-250

Other

FieldTypePurpose
scalenumberCurrent zoom — DO NOT edit by hand; Obsidian overwrites
closeboolWas the view closed; don't touch

What creates a graph edge

Before configuring color groups or filters, understand which references in a note become edges in the graph. Obsidian renders an edge from any of the following:

  • [[Wikilinks]] in the body of a note
  • Wikilinks inside frontmatter list fields, including topics: and categories: (any list-of-wikilinks pattern)
  • Wikilinks inside single-value frontmatter properties — e.g. author: "[[J. R. R. Tolkien]]", director: "[[Christopher Nolan]]", host: "[[Tim Ferriss]]". These generate graph edges identical to wikilinks in the body. This is often missed.
  • Tag nodes when showTags: true (otherwise tags don't appear at all)

What does NOT create an edge:

  • Plain string properties (role: "Senior Engineer")
  • Tags (when showTags: false)
  • Wikilinks inside fenced code blocks (```...```) or inline code (`)

This matters when you design color groups: a note with author: "[[J. R. R. Tolkien]]" is already linked to the J. R. R. Tolkien node, so an additional tag: tolkien adds nothing graph-wise. Similarly, a topics: entry is redundant if a property field already references the same target. See obsidian-graph-audit Pattern H.

Search syntax (for search filter and query in color groups)

Same syntax as core Search.

Operators

OperatorMatchesExample
path:"folder"Path contains stringpath:"Projects/Active"
file:"name"File namefile:"Home"
tag:#nameNote has tagtag:#hub
line:(query)Same lineline:(foo bar)
block:(query)Same blockblock:(query)
section:(query)Same heading sectionsection:(Notes)
task:(query) / task-todo: / task-done:Taskstask-todo:(review)
content:(query)Body only, not frontmattercontent:(meeting)
["property"]Note has property (substring, not strict — see caveat)["status"]
["property":"value"]Property contains value (substring)["categories":"Companies"]
/regex/Regex pattern/^Daily/
match-case: / ignore-case:Toggle case sensitivity

Logic

-fooNOT
foo bar (space)AND
foo OR barOR
"phrase"exact phrase
(foo OR bar)grouping

Caveats for color groups

  • Order matters. First matching group wins. Tags first (semantic role), categories next (entity type), topics third (epic clusters), paths last (catch-all).
  • ["property":"value"] is substring match, not strict. ["categories":"Recipe"] matches both Recipes and Recipe Drafts. Mitigation: use distinct strings as category/topic names. If strict match is needed, convert to a tag.
  • Regex is supported/^Daily/, /202\d-/, etc.

Color group format

{
  "query": "<search query>",
  "color": { "a": 1, "rgb": <decimal RGB> }
}
  • a — alpha (transparency), 0 = invisible, 1 = solid. Almost always 1.
  • rgb — 24-bit decimal: R*65536 + G*256 + B.

HEX → decimal conversion

python3 -c "h='E07B5A'; print(int(h, 16))"  # → 14711642

Or manually: 0xE07B5A = 224*65536 + 123*256 + 90 = 14,711,642.

One wrong byte renders as a different color. Always round-trip:

python3 -c "n=14711642; print(f'#{n:06X}')"  # → #E07B5A

Palette — pastel set

Soft, calm overall. Luminosity values are close, so 5+ groups in this set may visually merge.

ColorHEXDecimalUse
Coral#E07B5A14711642Active priority project
Pink#E36B9B14904219Personal / relationships
Purple#9B6BE310185699Habit / personal logs
Sky blue#5AAFE05943264Learning
Green#7BC15A8110426Lifestyle / household
Gold#E0C95A14731610Tech / sandbox
Teal#5EC1B56209973Planning
Yellow#F0D06015781984Hubs / MOCs
Cream#D9C89E14272670Zettelkasten / passive
Mustard#C9A03B13213755External entities
Soft gray#8B9DA99149865Clippings / refs
Muted blue-gray#708FA87376808Daily
Neutral gray#A0A0A010526880Inputs / rough
Dim gray#6666666710886Tag underlay

Palette — high-contrast set

Use when 5+ pastel groups blur together. Luminosity ranges from bright (hubs) to dark (Daily) — the eye separates them.

ColorHEXDecimalUse
Bright Gold#E8B93115252785Hubs (primary accent)
Coral red#D9583E14244414Active project (catch-all)
Mustard#B8861F12092447Entity type (Companies)
Light Purple#B47AC911828425Entity type (Contacts)
Deep Purple#7E4FA88278440Entity type (Concepts, MOCs)
Saturated Green#6FA8557317077Lifestyle category
Bright Cyan#2EB5C93061193Other projects (catch-all)
Cream#D9C89E14272670Passively saved (Clippings, Meetings)
Soft gray#8B9DA99149865Reports / refs
Dark Slate#4A5C6B4873323Daily / archive

Four coloring strategies

Color groups can target four different dimensions of a note. Different vaults call for different strategies; mature vaults usually mix them.

A. Folder-based (path:)

{"query": "path:\"Projects/Active\"", ...}

When to use: Project-organized vault where the folder is a meaningful unit. Each project lives in its own folder, the layout is stable.

Pros:

  • Simple mental model — "everything in this folder"
  • No frontmatter required
  • New files inherit the color automatically

Cons:

  • Doesn't capture cross-cutting concerns (hub-pages, status workflows) that span folders
  • Reorganizing folders breaks the coloring
  • Files in one folder may be of different types (an epic and a routine note get the same color)

B. Category-based (["categories":"X"])

{"query": "[\"categories\":\"Companies\"]", ...}

When to use: Kepano-style vault with frontmatter categories:. Category = entity type (company, contact, recipe, film). Files in one category are semantically homogeneous.

Pros:

  • Color follows entity meaning, not accidental location
  • A file can belong to multiple categories (multi-color via group order)
  • Folder reorganization doesn't break it

Cons:

  • Property-search is substring match. ["categories":"Recipe"] matches both Recipes and Recipe Drafts. Categories must be distinct strings (see Caveats)
  • Requires disciplined frontmatter
  • Files without categories (rough notes, drafts) are uncolored

C. Topic-based (["topics":"X"])

{"query": "[\"topics\":\"Active Initiative\"] OR file:\"Active Initiative\"", ...}

When to use: Kepano-style with topics: fields (wikilinks to epics or coordinating concepts). Useful for an active project with epic-coordinators and many files attached to each.

Pros:

  • Color = semantic cluster (the epic and everything tied to it)
  • Cross-folder: files from Reports/, Interview Prep/, BootCamp/ can land in one color if they share a topic
  • Ghost nodes work — the query reads the topic from frontmatter; it doesn't need the epic file to exist
  • Sharper than path catch-all for a large active project (splits work streams visually)

Cons:

  • Same substring-match caveat as ["categories":...]
  • Files without topics aren't colored
  • OR file:"X" is needed to color the epic node itself (the epic doesn't list itself as its own topic)
  • More groups means closer to the 14-group practical ceiling

D. Tag-based (tag:#name)

{"query": "tag:#hub", ...}

When to use: Tag taxonomy for cross-cutting concerns — hubs (#hub), statuses (#status/done), types (#moc, #evergreen). A tag describes "what role does this file play in the graph", not "what is this file".

Pros:

  • Strict match (not substring, unlike property)
  • Multi-dimensional — tags cross folders and categories
  • Easy to add or remove ad hoc

Cons:

  • Tag taxonomy can sprawl without discipline
  • A bare tag with no file is an orphan node when showTags: true
  • Bureaucratic for very large vaults

Mixed approach — typical for mature vaults

"colorGroups": [
  {"query": "tag:#hub", ...},                                                      // cross-cutting accent
  {"query": "[\"categories\":\"Companies\"]", ...},                                 // entity types
  {"query": "[\"categories\":\"Contacts\"]", ...},
  {"query": "[\"topics\":\"Active Initiative\"] OR file:\"Active Initiative\"", ...}, // epic clusters
  {"query": "[\"topics\":\"Other Initiative\"] OR file:\"Other Initiative\"", ...},
  {"query": "path:\"Daily\"", ...},                                                 // structural folder
  {"query": "path:\"Projects\"", ...}                                               // catch-all
]

Order matters — tags first (semantic role), categories second (entity type), topics third (epic clusters of the active project), paths last (catch-all). First match wins.

Choosing a strategy

SignalStrategy
Projects in Projects/X/, little frontmatterFolder-based
Kepano-style, categories: [[X]] everywhereCategory-based
Active project with epics, topics: on coordinatorsTopic-based
Goal: highlight hubs / statuses / workflow stagesTag-based
600+ notes, active project + entity types + epics + hubsMixed (all four together)

Best practices

  1. Color by meaning, not by folder. Folder is one dimension; see the four-strategy section above. Pick by structure, often combine.

  2. 3-8 color groups, not 14. Each group is render overhead and visual noise. If 14 feels needed, the user probably wants 5 conceptual buckets, not 14 individual ones.

  3. Order matters. Specific queries first, broad last. First match wins.

  4. Filter only for active noise, not for periphery. Folders that are weakly connected (rough notes, drafts, scratch) form their own peripheral cluster and don't crowd the center. Filter is justified when nodes from a folder embed into the center and pollute important clusters — typical candidates: Templates (if not in userIgnoreFilters), archive tagged #archive. Before adding -path: or -tag:, ask: "are these nodes currently in the center, or on the edge?". On the edge — leave them, they give visual segregation.

  5. Don't show tags on the graph unless they're cross-cutting. Tags isolated to a single note become satellite-nodes with no signal. Tags that duplicate folder names add tangle without meaning. Enable showTags: true only when tags form a real semantic network (#moc, #status/*, thematic concepts).

  6. textFadeMultiplier 0 means labels never visible. If you want to read names at zoom-in, set ≥ 0.3 (0.5 is a good default).

  7. Forces are vault-size dependent. Sane defaults for ~600 notes: centerStrength: 0.5, repelStrength: 15-25, linkStrength: 0.7-1, linkDistance: 150-250. Larger vault → more repel + shorter links.

  8. Don't edit scale by hand. Obsidian overwrites it on every zoom. Manual edits are wiped.

  9. Local graph is configured separately. Local graph config lives in .obsidian/workspace.json under workspace state; it doesn't inherit from global. For shared color groups locally, install the community plugin Sync Graph Settings.

  10. CSS snippet for defaults, color groups for exceptions (community pattern). Set base node/tag/attachment/unresolved colors via a CSS snippet once, use color groups only for specific subsets. Less retina-burning than 8 path-based groups doing everything.

CSS classes (for snippets)

The graph renders to canvas/WebGL, but Obsidian reads CSS color rules and converts them. Use !important to override. Path: Settings → Appearance → CSS snippets.

ClassWhat it colors
.graph-view.color-fillDefault node fill
.graph-view.color-fill-focusedFocused node
.graph-view.color-fill-tagTag node (theme-dependent: .theme-dark / .theme-light)
.graph-view.color-fill-attachmentAttachment
.graph-view.color-fill-unresolvedBroken link
.graph-view.color-fill-highlightHover
.graph-view.color-arrowDirection arrow
.graph-view.color-circleNode ring
.graph-view.color-lineEdge
.graph-view.color-line-highlightHover edge
.graph-view.color-textNode label

#HEX, rgb(), and rgba() are all supported. For transparent edges in graph view, use rgba.

Recipes

Hide noise

"search": "-path:\"Templates\" -path:\"Rough Notes\" -tag:#archive"

Focus on one project

"search": "path:\"Projects/Active\""

Color by status (tag-based)

"colorGroups": [
  {"query": "tag:#status/done",  "color": {"a": 1, "rgb": 8110426}},
  {"query": "tag:#status/draft", "color": {"a": 1, "rgb": 14731610}},
  {"query": "tag:#status/seed",  "color": {"a": 1, "rgb": 9149865}}
]

Color by project (folder-based)

"colorGroups": [
  {"query": "path:\"Projects/A\"", "color": {"a": 1, "rgb": 14711642}},
  {"query": "path:\"Projects/B\"", "color": {"a": 1, "rgb": 10185699}},
  {"query": "path:\"Notes\"",       "color": {"a": 1, "rgb": 14272670}},
  {"query": "path:\"Daily\"",       "color": {"a": 1, "rgb": 7376808}}
]

Color by category (Kepano-style)

"colorGroups": [
  {"query": "[\"categories\":\"Companies\"]", "color": {"a": 1, "rgb": 12092447}},
  {"query": "[\"categories\":\"Contacts\"]",  "color": {"a": 1, "rgb": 11828425}},
  {"query": "[\"categories\":\"Recipes\"]",   "color": {"a": 1, "rgb": 7317077}},
  {"query": "[\"categories\":\"Reports\"]",   "color": {"a": 1, "rgb": 9149865}}
]

Color by topic (epic clusters of an active project)

"colorGroups": [
  {"query": "[\"topics\":\"Initiative A\"] OR file:\"Initiative A\"",
   "color": {"a": 1, "rgb": 14904219}},
  {"query": "[\"topics\":\"Initiative B\"] OR file:\"Initiative B\"",
   "color": {"a": 1, "rgb": 5943264}}
]

OR file:"X" is needed because the epic node's own topics: references upstream parents, not itself.

Highlight hubs / MOCs

Tag-based — strict, recommended:

{"query": "tag:#hub", "color": {"a": 1, "rgb": 15252785}}

Folder/file-based fallback (when tags aren't in use):

{"query": "file:\"Home\" OR file:\"About\" OR path:\"Categories\"",
 "color": {"a": 1, "rgb": 15781984}}

Hide infrastructure and archive via tag taxonomy

Establish: #dashboard for view pages (kanban, metrics, charts), #no-graph for logs / setup docs / configs (pure control tag), #archive for archived content.

"search": "-tag:#dashboard -tag:#no-graph -tag:#archive"

Combine with category-exclude to hide entity types not wanted on the conceptual graph:

"search": "-tag:#dashboard -tag:#no-graph -[\"categories\":\"Vacancies\"]"

Forces for a 600+ note vault

"centerStrength": 0.5,
"repelStrength": 22,
"linkStrength": 0.7,
"linkDistance": 220,
"textFadeMultiplier": 0.5,
"nodeSizeMultiplier": 1.4,
"lineSizeMultiplier": 0.8

Caveats and gotchas

  • ⚠️ Obsidian rewrites graph.json when Graph view closes. When the graph tab closes (or the app quits), Obsidian flushes its in-memory state to the file, overwriting external edits. Symptom: just-made edits are gone, close: true, and colorGroups: [].

    Procedure: either (a) Cmd+Q quit Obsidian → edit graph.json → relaunch → open Graph view; or (b) close the Graph view tab → edit → Cmd+P → Reload app without saving → open Graph view. Never edit while Graph view is open.

  • Property-search is substring, not strict. ["topic"] matches topic, parent_topic, and topic_alternative. Don't use property search for color groups when precision matters; use tags instead.

  • Many color groups slow rendering. Each group is O(notes × groups) per repaint. Above 15 groups it's noticeable on large vaults.

  • showOrphans: false hides early sessions of a new vault. In a fresh vault most notes are orphans; without showOrphans: true you see an empty screen.

  • Filter cascading. Removing a note via filter also removes its edges, and its neighbors may become orphans on the graph.

  • Extended Graph plugin (ElsaTam/obsidian-extended-graph, locally-installed) gives deeper config (per-tag colors, edge weights). Recommend it when core hits a ceiling — don't try to squeeze more from the core graph.

Multiple views / saved presets

Core Obsidian doesn't support saved graph presets — one global config, one local. To switch between multiple views, install a plugin:

PluginWhat it doesInstallWhen to pick
Extended Graph (ElsaTam)Multiple views + switching, per-tag colors, image nodes, advanced filters, SVG exportCommunity storeWhen you want both presets and a stronger graph overall
Graph Presets (ycnmhd)Markdown-based: preset = .md with YAML, apply / update from active graphBRAT (not in community store)When you want presets as notes in the vault, versioned in git
Persistent GraphSaves node positions + filters + workspacesCommunity storeWhen fixing the layout matters, not just settings
Sync Graph SettingsSync global → local color groupsCommunity storeWhen you use local graph often; not for presets

Don't recommend a plugin preemptively. Have the user describe the views they want to switch between (e.g. "focused project view" vs "full vault"). If there's no concrete use case, one global config is enough.

Manual approach without a plugin: store variants of graph.json in .obsidian/graph-presets/ and swap with a script. Not recommended — no UI for switching, easy to forget to restore, and races with Obsidian's overwrite (see Caveats).

When NOT to touch the graph

  • The graph is used for 5-10 minutes a day for serendipity, not as primary navigation. Don't optimize it as a dashboard.
  • The user hasn't said what's bothering them. Don't propose edits unprompted.
  • Aesthetic edits without a clear task = bikeshedding. Get the goal first ("I want to see X / I don't want to see Y"), then config.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.