{
    "name": "wiki-dashboard",
    "version": "1.0.0",
    "description": "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.",
    "system_prompt": "name wiki-dashboard description 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. Wiki Dashboard — Dynamic Vault Views 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. Before You Start Writing profile: Before drafting or rewriting natural-language Markdown, read and apply the Writing Profile Resolution section in llm-wiki/SKILL.md . Framework schema, provenance, safety, and operation-specific requirements take precedence. Apply WRITING.md preferences only to optional Markdown dashboard prose; .base syntax remains unchanged. Resolve config — follow the Config Resolution Protocol in llm-wiki/SKILL.md (inline @name override → walk up CWD for .env → global config → prompt setup). This gives OBSIDIAN_VAULT_PATH . Read $OBSIDIAN_VAULT_PATH/index.md to understand what categories and pages exist. Ask the user what they want to view if not specified — folder, tag, category, date range? Ask if they have Dataview installed if you're unsure which tool to use. Option A — Obsidian Bases ( .base files) Bases are YAML files that define live views over vault notes. Native to Obsidian 1.8+, no plugin needed. Official canonical schema 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 Filter syntax — CRITICAL 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\") Property name conventions 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 — Basic table — folder filter 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 Cards view — folder filter 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 Group by property — groupBy goes INSIDE the view 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 Tag filter 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 Multi-filter (folder AND tag) 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 OR filter (two folders) 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 Computed column via formulas 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 Filter expression reference 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. Option B — Dataview (community plugin) 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. Basic table — folder ```dataview TABLE tags AS \"Tags\", summary AS \"Summary\", file.mtime AS \"Last Modified\" FROM \"concepts\" SORT file.mtime DESC ``` Table with clickable links (TABLE WITHOUT ID) ```dataview TABLE WITHOUT ID file.link AS \"Entity\", tags AS \"Tags\", summary AS \"Summary\" FROM \"entities\" SORT file.name ASC ``` GROUP BY — use rows. prefix after grouping After GROUP BY , individual file properties must be prefixed with rows. — otherwise the column is empty or errors. ```dataview TABLE WITHOUT ID rows.file.link AS \"Concept\", rows.summary AS \"Summary\" FROM \"concepts\" GROUP BY tags[0] AS \"Domain\" ``` Stale pages — use file.mtime for date math Avoid choice(updated, date(updated), file.mtime) — mixed date formats in updated frontmatter cause arithmetic errors. file.mtime is always a valid DateTime. ```dataview 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 ``` Multi-folder query ```dataview TABLE summary AS \"Summary\", file.mtime AS \"Last Modified\" FROM \"projects\" WHERE file.name != file.folder SORT file.mtime DESC ``` Dataview reference 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 Step 3: Write the File 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: \"All concepts\" → _meta/concepts-index.base \"Recent ingests\" → _meta/recent-ingests.base \"Project overview\" → _meta/projects-overview.base \"Stale pages\" → _meta/stale-pages.base \"Full dashboard\" → _meta/dashboard.md Create _meta/ if it doesn't exist yet. Step 4: Embed Bases (optional) To embed a .base inside a note: ## Entities ![[ _meta/entities-tracker.base]] Ask before modifying an existing note. Step 5: Update Tracking 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. Common Dashboard Recipes 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 Quality Checklist Bases: filters use expression strings under and: / or: / not: , never typed objects Bases: groupBy goes inside the view definition — not as a top-level key Bases: column headers set via properties: <name>: displayName: \"...\" , not columns: [{title}] Bases: formulas: used for computed columns, referenced as formula.<name> in order/properties Dataview: GROUP BY queries use rows.property not bare property Dataview: date arithmetic uses file.mtime , not choice(updated, ...) File written to _meta/ with a descriptive slug log.md updated User told how to embed Bases ( ![[_meta/<name>.base]] ) or open the dashboard note QMD Refresh After Vault Writes QMD 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",
    "model_config": {
        "provider": "deepseek",
        "model": "deepseek-chat",
        "temperature": 0.7,
        "max_tokens": 4096,
        "top_p": 0.9
    },
    "trigger_words": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=ar9av-obsidian-wiki-skills-wiki-dashboard-skill-md"
}