> Create dynamic, queryable dashboard views of the Obsidian vault using Obsidian Bases or Dataview. Use this skill when the user says "create a dashboard", "vault dashboard", "show all X as a table", "dynamic view", "query my vault", "build a content index", "show me all concepts/entities/projects", or wants a structured, auto-updating view of their wiki content. Bases is native to Obsidian 1.8+ (no plugin needed). Dataview requires the community plugin.
npx skills add https://github.com/Ar9av/obsidian-wiki --skill wiki-dashboard
Two tools available: Obsidian Bases (native, GUI-driven, no plugin) and Dataview (community plugin, SQL-like, more powerful). Check which the user has and prefer Bases unless they ask for Dataview or need GROUP BY / computed columns.
llm-wiki/SKILL.md (inline @name override → walk up CWD for .env → ~/.obsidian-wiki/config → prompt setup). This gives OBSIDIAN_VAULT_PATH.$OBSIDIAN_VAULT_PATH/index.md to understand what categories and pages exist..base files)Bases are YAML files that define live views over vault notes. Native to Obsidian 1.8+, no plugin needed.
Top-level keys:
filters: # Global filter applied to all views (expression strings under and/or/not)
formulas: # Named computed properties — referenced as formula.<name>
properties: # Display config per property — sets displayName for column headers
summaries: # Aggregation formulas (e.g. mean, sum)
views: # Array of view definitions (required)
Each item in views::
views:
- type: table # table | list | cards | map
name: "View Name" # display label
limit: 50 # optional max rows
order: # column display order (list of property/formula names)
- file.name
- note.updated
groupBy: # grouping — goes INSIDE the view, NOT at top level
property: note.tags
direction: ASC # ASC | DESC
filters: # view-specific filter (merges with global filters)
and:
- 'note.status != "done"'
summaries:
formula.myFormula: Average
Filters use expression strings, not typed objects. Always wrap in and:, or:, or not: — a bare list causes a "may only have one of and/or/not keys" parse error.
# CORRECT
filters:
and:
- file.inFolder("concepts")
# WRONG — typed objects (parse error)
filters:
- type: folder
folder: concepts
Filters support nesting:
filters:
or:
- file.hasTag("book")
- and:
- file.inFolder("concepts")
- file.hasTag("research")
- not:
- file.hasTag("archived")
Different contexts use different naming — confirmed from Obsidian's auto-reformat behaviour:
| Context | Frontmatter field tags | File name | Formula |
|---|---|---|---|
| properties: keys | note.tags | file.name | formula.<name> |
| order: values | tags (bare) | file.name | formula.<name> |
| groupBy.property: | tags (bare) | file.name | — |
| filters: expressions | file.hasTag(...) / note.tags | file.name | formula.<name> |
| formulas: expressions | note.tags, note.updated | file.name | — |
filters:
and:
- file.inFolder("concepts")
properties:
file.name:
displayName: Page
note.tags:
displayName: Tags
note.summary:
displayName: Summary
note.updated:
displayName: Updated
views:
- type: table
name: Table
order:
- file.name
- tags
- summary
- updated
filters:
and:
- file.inFolder("entities")
properties:
file.name:
displayName: Entity
note.title:
displayName: Full Name
note.tags:
displayName: Tags
note.summary:
displayName: Summary
views:
- type: cards
name: Cards
order:
- file.name
- title
- tags
- summary
When groupBy is set, omit that property from order: — it becomes the group header row and adding it as a column too causes duplication.
filters:
and:
- file.inFolder("concepts")
properties:
file.name:
displayName: Concept
note.summary:
displayName: Summary
note.updated:
displayName: Updated
views:
- type: table
name: By Domain
groupBy:
property: tags # bare property name, no note. prefix
direction: ASC
order:
- file.name # do NOT include tags here — already the group header
- summary
- updated
filters:
and:
- file.hasTag("machine-learning")
properties:
file.name:
displayName: Page
note.category:
displayName: Category
note.summary:
displayName: Summary
views:
- type: table
name: Table
order:
- file.name
- category
- summary
filters:
and:
- file.inFolder("projects")
- file.hasTag("active")
properties:
file.name:
displayName: Project
note.summary:
displayName: Summary
note.updated:
displayName: Last Updated
views:
- type: cards
name: Cards
order:
- file.name
- summary
- updated
filters:
or:
- file.inFolder("concepts")
- file.inFolder("entities")
properties:
file.name:
displayName: Page
note.category:
displayName: Category
note.updated:
displayName: Updated
views:
- type: table
name: Table
order:
- file.name
- category
- updated
filters:
and:
- file.inFolder("concepts")
formulas:
days_stale: "floor((now() - note.updated) / 86400000)"
properties:
file.name:
displayName: Page
note.updated:
displayName: Updated
formula.days_stale:
displayName: Days Stale
views:
- type: table
name: Stale
order:
- file.name
- updated
- formula.days_stale
| Expression | What it does |
|---|---|
| file.inFolder("path") | Pages in that folder |
| file.hasTag("tag") | Pages with that tag (no # prefix) |
| file.hasLink("Note Name") | Pages linking to a note |
| file.name == "note-name" | Exact filename match |
| file.ext == "md" | Filter by extension |
| note.propertyName | Any frontmatter property |
| formula.formulaName | A named formula result |
| now() | Current timestamp in ms |
> On Obsidian UI-generated format: When Obsidian's GUI writes or reformats a .base file it may output a simplified shorthand with top-level columns:, sort:, and view: keys instead of the canonical schema. That format also works — Obsidian accepts both. Manually authored files should use the canonical schema above.
Dataview uses a SQL-like query language inside dataview code blocks in any note. More powerful than Bases for computed columns, GROUP BY, and cross-folder queries.
TABLE
tags AS "Tags",
summary AS "Summary",
file.mtime AS "Last Modified"
FROM "concepts"
SORT file.mtime DESC
TABLE WITHOUT ID
file.link AS "Entity",
tags AS "Tags",
summary AS "Summary"
FROM "entities"
SORT file.name ASC
rows. prefix after groupingAfter GROUP BY, individual file properties must be prefixed with rows. — otherwise the column is empty or errors.
TABLE WITHOUT ID
rows.file.link AS "Concept",
rows.summary AS "Summary"
FROM "concepts"
GROUP BY tags[0] AS "Domain"
file.mtime for date mathAvoid choice(updated, date(updated), file.mtime) — mixed date formats in updated frontmatter cause arithmetic errors. file.mtime is always a valid DateTime.
TABLE WITHOUT ID
file.link AS "Page",
category AS "Type",
file.mtime AS "Last Modified",
(date(today) - file.mtime).days + " days" AS "Age"
FROM "concepts" OR "entities" OR "projects"
WHERE file.name != file.folder
WHERE (date(today) - file.mtime).days > 30
SORT (date(today) - file.mtime).days DESC
TABLE
summary AS "Summary",
file.mtime AS "Last Modified"
FROM "projects"
WHERE file.name != file.folder
SORT file.mtime DESC
| Clause | Usage |
|---|---|
| FROM "folder" | All notes in folder |
| FROM #tag | All notes with tag |
| FROM "a" OR "b" | Union of two folders |
| WHERE file.name != file.folder | Exclude folder index pages |
| GROUP BY field AS "Label" | Group rows — use rows. for properties after this |
| SORT field DESC | Sort direction |
| file.link | Clickable wikilink |
| file.mtime | Last modified time (always valid DateTime) |
| (date(today) - file.mtime).days | Days since last modification |
Bases: Target path $OBSIDIAN_VAULT_PATH/_meta/<dashboard-name>.base
Dataview: Write queries directly into any .md note. A dedicated dashboard note at $OBSIDIAN_VAULT_PATH/_meta/dashboard.md works well for multi-section views.
Slug examples:
_meta/concepts-index.base_meta/recent-ingests.base_meta/projects-overview.base_meta/stale-pages.base_meta/dashboard.mdCreate _meta/ if it doesn't exist yet.
To embed a .base inside a note:
## Entities
![[_meta/entities-tracker.base]]
Ask before modifying an existing note.
Append to $OBSIDIAN_VAULT_PATH/log.md:
- [TIMESTAMP] WIKI_DASHBOARD name="<slug>" tool=bases|dataview view=<type> filter="<description>"
No manifest or index update needed — dashboards are live queries, not static pages.
| Dashboard | Best tool | What it shows |
|---|---|---|
| Content index | Bases or Dataview | All pages grouped by category, sorted by updated |
| Entity tracker | Bases (cards) | Entity pages as a visual card gallery |
| Concepts by domain | Dataview | Concepts grouped by first tag using GROUP BY |
| Ingestion log | Either | Pages sorted by created date |
| Stale content | Dataview | Pages not touched in 30+ days with day count |
| Project overview | Either | Project pages with last-sync date |
| Research tracker | Dataview | Synthesis pages tagged research |
and:/or:/not:, never typed objectsgroupBy goes inside the view definition — not as a top-level keyproperties: <name>: displayName: "...", not columns: [{title}]formulas: used for computed columns, referenced as formula.<name> in order/propertiesrows.property not bare propertyfile.mtime, not choice(updated, ...)_meta/ with a descriptive sluglog.md updated![[_meta/<name>.base]]) or open the dashboard noteQMD is a search index, not the source of truth. If $QMD_WIKI_COLLECTION is empty or unset, skip this step. Run it only after this skill has written or rewritten vault markdown. If QMD refresh fails, do not roll back the vault changes; report the QMD status separately.
Use $QMD_CLI if set; otherwise use qmd.
${QMD_CLI:-qmd} update
If the output says vectors are needed or embeddings may be stale, run:
${QMD_CLI:-qmd} embed
Verify the collection with either:
${QMD_CLI:-qmd} ls "$QMD_WIKI_COLLECTION"
or, when a specific page path is known:
${QMD_CLI:-qmd} get "qmd://$QMD_WIKI_COLLECTION/<page>.md" -l 5
Record one of:
QMD refreshed: update + embed + verifiedQMD refreshed: update only + verifiedQMD skipped: QMD_WIKI_COLLECTION unsetQMD skipped: qmd CLI unavailableQMD failed: <short error summary>Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
Picks random winners from lists, spreadsheets, or Google Sheets for giveaways, raffles, and contests. Ensures fair, unbiased selection with transparency.
Query openFDA API for drugs, devices, adverse events, recalls, regulatory submissions (510k, PMA), substance identification (UNII), for FDA regulatory data analysis and safety research.
MATLAB and GNU Octave numerical computing for matrix operations, data analysis, visualization, and scientific computing. Use when writing MATLAB/Octave scripts for linear algebra, signal processing, image processing, differential equations, optimization, statistics, or creating scientific visualizations. Also use when the user needs help with MATLAB syntax, functions, or wants to convert between MATLAB and Python code. Scripts can be executed with MATLAB or the open-source GNU Octave interpreter.
UMAP dimensionality reduction. Fast nonlinear manifold learning for 2D/3D visualization, clustering preprocessing (HDBSCAN), supervised/parametric UMAP, for high-dimensional data.
Creating interactive data visualisations using d3.js. This skill should be used when creating custom charts, graphs, network diagrams, geographic visualisations, or any complex SVG-based data visualisation that requires fine-grained control over visual elements, transitions, or interactions. Use this for bespoke visualisations beyond standard charting libraries, whether in React, Vue, Svelte, vanilla JavaScript, or any other environment.
Access AlphaFold 200M+ AI-predicted protein structures. Retrieve structures by UniProt ID, download PDB/mmCIF files, analyze confidence metrics (pLDDT, PAE), for drug discovery and structural biology.
Take ar9av/wiki-dashboard from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.