Best for
- Use when the user says "set up agentic OS in Obsidian", "install command center in my vault", "build a vault dashboard", "give me my own versio
naveedharri/benai-skills/shared-skills/agentic-os-obsidian/SKILL.md
Set up an agentic OS inside an Obsidian vault — a configurable command-center dashboard with 5 auto-installed, bundled plugins (Dataview, CustomJS, Shell-commands, Terminal, Homepage), Home + per-profile + Vault Overview pages, KPI cards, sparklines, heatmap, task rollup, and a button bar wired to user-defined Claude prompts. Markdown-native, no servers. Use when the user says "set up agentic OS in Obsidian", "install command center in my vault", "build a vault dashboard", "give me my own versio
Decision brief
Installs a configurable command-center dashboard inside an existing Obsidian vault. Bundles and side-loads 5 plugins (Dataview, CustomJS, Shell-commands, Terminal, Homepage). Home + per-profile + Vault Overview pages, KPI cards, sparklines, heatmap, task rollup, recent-files lis…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/naveedharri/benai-skills --skill "shared-skills/agentic-os-obsidian"Inspect the Agent Skill "agentic-os-obsidian" from https://github.com/naveedharri/benai-skills/blob/81c92a39f3c7be5754b9bc7efee6aaa5627d535c/shared-skills/agentic-os-obsidian/SKILL.md at commit 81c92a39f3c7be5754b9bc7efee6aaa5627d535c. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Step 1. Create folder structure” section in the pinned source before continuing.
Read $SKILLDIR/references/obsidian/dashboard-template.js. Substitute each placeholder with the captured value:
If a brand mark was provided in Q10, copy it into Dashboard/lib/.
For each page in PAGES, read the corresponding template and substitute placeholders:
references/obsidian/setup-template.md → Dashboard/Setup.md, substitute {{BRANDLABEL}}, {{PAGESLINKS}}
Permission review
The documentation asks the agent to run terminal commands or scripts.
node -e "$(cat dashboard-template.js | sed 's/__[A-Z_]*__/null/g' | python3 -c 'import sys; print(f\"({sys.stdin.read()})\");')"The documentation asks the agent to create, modify, or delete local files.
### Step 1. Create folder structureThe documentation asks the agent to run terminal commands or scripts.
node --check Dashboard/components/dashboard.js || { echo "FATAL: dashboard.js has a syntax error"; exit 1; }The documentation asks the agent to read local files, directories, or repositories.
node -e "try { new Function('return (' + require('fs').readFileSync('Dashboard/components/dashboard.js','utf8') + ')'); console.log('CustomJS wrap OK'); } catch (e) { console.error('FATAL — CustomJS will reject this file:', e.message); procEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 87/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 48 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Installs a configurable command-center dashboard inside an existing Obsidian vault. Bundles and side-loads 5 plugins (Dataview, CustomJS, Shell-commands, Terminal, Homepage). Home + per-profile + Vault Overview pages, KPI cards, sparklines, heatmap, task rollup, recent-files lists, and a button bar wired to user-defined Claude prompts via the Shell-commands plugin. All 5 required plugins are bundled and auto-enabled — the user only has to restart Obsidian. Markdown-based, no servers.
Looking for a real web app with live API integrations and a Railway URL instead? That's the standalone route — use the
agentic-os-standaloneskill.
{vault}/
├── Dashboard/
│ ├── Home.md # Team-wide dashboard
│ ├── Vault-Overview.md # Top-level vault health view
│ ├── {Profile}.md # One per configured profile
│ ├── Setup.md # In-vault setup reference
│ ├── CLAUDE.md # AI routing rules for this folder
│ ├── components/dashboard.js # The renderer (CONFIG substituted)
│ └── lib/frappe-charts.min.js
└── .obsidian/
├── snippets/command-center.css # Brand-styled CSS (enabled)
├── plugins/dataview/ # Side-loaded, enabled
├── plugins/customjs/ # Side-loaded, enabled
├── plugins/obsidian-shellcommands/ # Side-loaded, enabled, aliases pre-registered
├── plugins/terminal/ # Side-loaded, enabled, "Claude" profile configured
├── plugins/homepage/ # Side-loaded, enabled, points to Dashboard/Home
├── community-plugins.json # Union'd with the 5 plugin IDs
└── appearance.json # CSS snippet enabled
test -d .obsidian && echo "vault confirmed" || (echo "NOT A VAULT — cd into your Obsidian vault first" && exit 1)
test -d Dashboard && echo "DASHBOARD EXISTS — ask user before overwriting"
If Dashboard/ exists, ask: cancel / install into Dashboard-new/ / overwrite (require explicit "yes overwrite").
Use AskUserQuestion for each. Capture answers into variables for substitution into CONFIG.
"What's your organization or personal brand name? (Used in folder paths and dashboard header. Short — one word ideal.)"
Default if skipped: MyOrg. Variable: ORG_NAME.
"What should the dashboard show as its title?" Two parts:
ORG_NAME. Variable: BRAND_LABEL.Command Center. Variable: BRAND_SUB."Who's on the team? List each profile name on a separate line. Use lowercase or capitalized — must match the folder names you'll use in step 4. For a solo vault, just one name."
Parse into array. Variable: PROFILES (JSON array). The first is DEFAULT_PROFILE.
"Where do you keep per-profile data?" Show 4 options via AskUserQuestion:
Team/{ORG}/Profiles/{name} (recommended for orgs — matches Team/Acme/Profiles/Alex)Team/{name} (flat team layout)People/{name} (alternative naming){name}. {ORG} is optional.Variable: PROFILE_FOLDER_PATTERN. Verify each PROFILES[i] folder exists (warn if missing — installer will still write the dashboard; profile folders just won't have data yet).
Three quick AskUserQuestion calls (each with sensible defaults):
Daily. Empty disables. Variable: DAILY_SUBPATH.task-list). Default task-list. Empty disables the task widget. Variable: TASKS_SUBPATH.snapshots. Variable: SNAPSHOTS_SUBPATH.Also: Root daily path for team-wide rollups. Default Daily. Variable: ROOT_DAILY_PATH.
Which dashboard pages? MultiSelect:
Variable: PAGES (array). Installer creates one .md per page.
"Name 3-5 top-level folders to show on the Vault Overview page. For each: folder path, one-line description, optional CLAUDE.md path (or empty)."
Variable: OVERVIEW_FOLDERS (array of [path, description, claude_path]).
Defaults if skipped: detect Projects/, Resources/, Intelligence/, Departments/, Teams/, Daily/ if present.
"What action buttons should the dashboard expose? For each button: label (short), icon (emoji or empty), scope (team / profile / both), and a Claude prompt (the text passed to claude -p '...'). Use {profile} in the prompt to interpolate the current profile name. Add as many as you want; defaults are zero."
Suggest these as starter buttons (user can accept/reject each):
"Read today's daily for {profile} if it exists. Write a 5-line morning brief covering open loops, top priorities, and energy level. Append under a '## Morning brief' heading.")"Create today's daily note at <DAILY_PATH>/$(date +%Y-%m-%d).md with standard frontmatter and section headings. Don't overwrite if it exists.")cmd: terminal:open-terminal.claude.root) — wired via Terminal plugin "Claude" profile, NOT a shell-commandcmd: app:reload)cmd: app:open-settings)For every button with a prompt, the installer generates a shell-commands alias shell-command-<kebab-label> and a cmd: obsidian-shellcommands:shell-command-<alias>.
Variable: BUTTONS (object with team and profile arrays).
Two hex colors. Defaults: primary #020309, canvas #FAF3E3. Substitute into CONFIG.COLORS and the CSS snippet variables at the top of command-center.css.
"Path to a PNG/SVG logo to show in the dashboard header? Leave blank for no logo."
If provided, copy to Dashboard/lib/brand-mark.png and set BRAND_MARK_PATH to that. Default empty.
These two pitfalls broke real installs. Treat them as load-bearing.
CustomJS evaluates each .js file as eval(\(${file_contents})`)` — it wraps the entire file in parentheses to coerce it into a single expression and pull the class out. This means:
class dashboard { ... } declaration. Comments before and after are fine.const CONFIG = {...} or any other statement before/after the class. (const CONFIG = ...) is a syntax error because const is a statement, not an expression. CustomJS surfaces this as SyntaxError: ParseError: Unexpected token.In references/obsidian/dashboard-template.js, CONFIG lives as a static CONFIG = { ... } field at the top of the class body. All code references it as this.constructor.CONFIG.X (not bare CONFIG.X). Preserve that pattern when editing — if you re-introduce a top-level const, the dashboard renders as a blank page with no console error from the dataviewjs block.
Smoke test before shipping any renderer change:
node -e "$(cat dashboard-template.js | sed 's/__[A-Z_]*__/null/g' | python3 -c 'import sys; print(f\"({sys.stdin.read()})\");')"
If that fails with a SyntaxError, the file violates the constraint. Fix before substitution.
Always cross-reference the keys against a known-working vault — do not invent them from the plugin's UI labels. Confirmed-correct keys:
| Plugin | Key | Wrong-but-tempting alternative |
|---|---|---|
| customjs | jsFolder (and jsFiles: "") | ❌ folder (silently ignored — plugin scans nothing) |
| dataview | enableDataviewJs, enableInlineDataviewJs, enableInlineDataview | (these are correct) |
| homepage | homepages (plural, object keyed by display name) → .value | ❌ homepage (singular) |
| terminal | profiles.claude.executable (absolute path) | ❌ relying on claude in PATH (Obsidian doesn't inherit login shell PATH) |
| obsidian-shellcommands | shell_commands[shell-command-<alias>].shell_command | ❌ flat commands[] array |
When in doubt, look at a known-working install's data.json before writing the install flow.
After the interview, do these in order. Resolve $SKILL_DIR from the skill's own path. All references live under $SKILL_DIR/references/obsidian/.
mkdir -p Dashboard/components Dashboard/lib Dashboard/snapshots
mkdir -p .obsidian/snippets .obsidian/plugins
Read $SKILL_DIR/references/obsidian/dashboard-template.js. Substitute each placeholder with the captured value:
| Placeholder | Value |
|---|---|
__ORG_NAME__ | ORG_NAME |
__BRAND_LABEL__ | BRAND_LABEL |
__BRAND_SUB__ | BRAND_SUB |
__BRAND_MARK_PATH__ | BRAND_MARK_PATH (or "") |
__PROFILES_JSON__ | JSON.stringify(PROFILES) |
__DEFAULT_PROFILE__ | DEFAULT_PROFILE |
__PROFILE_FOLDER_PATTERN__ | PROFILE_FOLDER_PATTERN |
__DAILY_SUBPATH__ | DAILY_SUBPATH |
__TASKS_SUBPATH__ | TASKS_SUBPATH |
__SNAPSHOTS_SUBPATH__ | SNAPSHOTS_SUBPATH |
__ROOT_DAILY_PATH__ | ROOT_DAILY_PATH |
__OVERVIEW_FOLDERS_JSON__ | JSON.stringify(OVERVIEW_FOLDERS) |
__BUTTONS_JSON__ | JSON.stringify(BUTTONS) |
__CLAUDE_PROMPTS_JSON__ | "{}" (reserved for later use) |
__SKILLS_FOLDER__ | optional skills folder path (e.g. Plugins/skills); empty hides the Vault Overview Skills section |
__SKILL_GROUPS_JSON__ | "{}" (or JSON.stringify({"Group": ["skill-a", "skill-b"]})) |
__PROJECT_CATEGORIES_JSON__ | "[]" (or JSON.stringify(["Agency","Content"]) if user has Projects/<Cat>/ subfolders) |
__CONNECTORS_JSON__ | "[]" (or JSON.stringify([["Slack","Team comms"], ...])) |
__CHEATSHEET_JSON__ | "[]" (or JSON.stringify([["Meeting note","Intelligence/meetings/YYYY-MM-DD-{Title}.md"], ...])) |
Ask the user about these five optional Vault Overview fields only if they chose to ship the Vault Overview page. All accept empty defaults.
Write the result to Dashboard/components/dashboard.js. Verify with both checks before declaring success:
# 1. Plain JS parse
node --check Dashboard/components/dashboard.js || { echo "FATAL: dashboard.js has a syntax error"; exit 1; }
# 2. CustomJS-wrapped parse — catches the 'top-level const' pitfall (see Critical Constraints)
node -e "try { new Function('return (' + require('fs').readFileSync('Dashboard/components/dashboard.js','utf8') + ')'); console.log('CustomJS wrap OK'); } catch (e) { console.error('FATAL — CustomJS will reject this file:', e.message); process.exit(1); }"
Both must pass. If check #2 fails but #1 passes, you've reintroduced a top-level statement before the class.
cp "$SKILL_DIR/references/obsidian/frappe-charts.min.js" Dashboard/lib/
If a brand mark was provided in Q10, copy it into Dashboard/lib/.
For each page in PAGES, read the corresponding template and substitute placeholders:
references/obsidian/home-template.md → Dashboard/Home.mdreferences/obsidian/vault-overview-template.md → Dashboard/Vault-Overview.mdPROFILES: references/obsidian/profile-template.md → Dashboard/{Profile}.md, substitute {{PROFILE_NAME}}references/obsidian/setup-template.md → Dashboard/Setup.md, substitute {{BRAND_LABEL}}, {{PAGES_LINKS}}references/obsidian/claude-md-template.md → Dashboard/CLAUDE.md, substitute {{ORG_NAME}}, {{PROFILE_FOLDER_PATTERN}}For each of dataview, customjs, obsidian-shellcommands, terminal, homepage:
if [ ! -d ".obsidian/plugins/$ID" ]; then
mkdir -p ".obsidian/plugins/$ID"
cp "$SKILL_DIR/references/obsidian/plugins/$ID/"* ".obsidian/plugins/$ID/"
fi
The if guard preserves any newer version the user already has.
dataview:
{"enableDataviewJs": true, "enableInlineDataviewJs": true, "enableInlineDataview": true}
customjs: The key is jsFolder (NOT folder).
{"jsFiles": "", "jsFolder": "Dashboard/components", "startupScriptNames": [], "registeredInvocableScriptNames": []}
obsidian-shellcommands: For each button in BUTTONS.team and BUTTONS.profile that has a prompt, generate one shell-commands entry:
{
"shell_commands": {
"shell-command-<alias>": {
"shell_command": "cd \"{{vault_dir}}\" && claude -p \"<escaped-prompt>\" --dangerously-skip-permissions",
"alias": "<alias>",
"platform_specific_commands": {"default": "use this"},
"shells": {},
"events": {},
"debounce": null,
"execution_notification_mode": "disabled",
"output_channels": {"stdout": "notification", "stderr": "notification-bigger"},
"output_handlers": {},
"output_wrappers": {"stdout": null, "stderr": null},
"command_palette_availability": "enabled"
}
},
"preferences": {"debug": false},
"settings_version": "0.23.0"
}
Replace <alias> with kebab-cased label. Escape double quotes in the prompt.
terminal: Cross-platform "claude" integrated profile.
Resolving the claude binary path — #1 source of Launch Claude button failures. Obsidian's Terminal plugin spawns processes without the user's login shell, so PATH is not enough. Need absolute path.
Probe in this order, take the first hit:
CLAUDE_BIN="$(zsh -ic 'command -v claude' 2>/dev/null || bash -ic 'command -v claude' 2>/dev/null || command -v claude)"
if [ -z "$CLAUDE_BIN" ] || [ ! -x "$CLAUDE_BIN" ]; then
for candidate in \
"$HOME/.local/bin/claude" \
"$HOME/.claude/local/claude" \
"/opt/homebrew/bin/claude" \
"/usr/local/bin/claude" \
"$HOME/.npm-global/bin/claude" \
"$HOME/.volta/bin/claude" \
"/usr/bin/claude"; do
[ -x "$candidate" ] && CLAUDE_BIN="$candidate" && break
done
fi
if [ -n "$CLAUDE_BIN" ] && "$CLAUDE_BIN" --version >/dev/null 2>&1; then
echo "resolved: $CLAUDE_BIN"
else
CLAUDE_BIN=""
fi
If empty, fall through to AskUserQuestion asking for the full path or to skip. If skipped, omit the claude profile entirely rather than ship a broken one.
{
"profiles": {
"claude": {
"args": ["--dangerously-skip-permissions"],
"executable": "<resolved absolute path>",
"followTheme": true,
"name": "Claude",
"platforms": {"darwin": true, "linux": true, "win32": true},
"restoreHistory": false,
"rightClickAction": "copyPaste",
"successExitCodes": ["0", "SIGINT", "SIGTERM"],
"terminalOptions": {"documentOverride": null},
"type": "integrated",
"useWin32Conhost": true
}
},
"addToCommand": true,
"focusOnNewInstance": true,
"newInstanceBehavior": "newHorizontalSplit"
}
Print the resolved path to the user in the final summary (Launch Claude wired to: /opt/homebrew/bin/claude). Verify before declaring complete: test -x "$CLAUDE_BIN" and "$CLAUDE_BIN" --version. If a user later sees a Python FileNotFoundError stack from the terminal pane, the executable moved — recovery: re-run which claude, edit .obsidian/plugins/terminal/data.json → profiles.claude.executable, reload Obsidian. Skill should offer to rewire just this step if re-invoked on an existing install.
homepage:
{
"version": 4,
"homepages": {
"Main Homepage": {
"value": "Dashboard/Home",
"kind": "File",
"openOnStartup": true,
"openMode": "Replace all open notes",
"manualOpenMode": "Replace all open notes",
"view": "Reading view",
"revertView": true,
"openWhenEmpty": true,
"refreshDataview": true,
"autoCreate": false,
"autoScroll": false,
"pin": false,
"commands": [],
"alwaysApply": false,
"hideReleaseNotes": false
}
},
"separateMobile": false
}
Read .obsidian/community-plugins.json (create [] if missing). Backup to .bak. Union in ["dataview", "customjs", "obsidian-shellcommands", "terminal", "homepage"]. Preserve existing entries. Write back.
Read .obsidian/appearance.json (create {} if missing). Backup to .bak. Read references/obsidian/command-center.css, substitute color placeholders, write to .obsidian/snippets/command-center.css. Set enabledCssSnippets to union with ["command-center"].
Done. The dashboard, all 5 plugins, the Claude terminal profile, and the homepage are configured.
ONE final step Obsidian only picks up on launch:
1. Quit Obsidian fully (Cmd/Ctrl + Q — not just close the window).
2. Reopen the vault.
3. Obsidian will ask you to "Trust" each plugin. Click Trust for all 5.
4. Open Dashboard/Home — it should render immediately.
If a button doesn't fire, open Settings → Shell commands and confirm each alias matches the one shown in the button's tooltip. The dashboard's developer console (Cmd/Ctrl + Opt/Alt + I) logs every command attempt.
Dashboard/ and .obsidian/. The dashboard is read-only on Daily/, Team/, Tasks/, etc..obsidian/plugins/<id>/ installs — only side-load when missing. The user may have a newer version.| Template | Goes to | Substitutes |
|---|---|---|
obsidian/dashboard-template.js | Dashboard/components/dashboard.js | All __*__ placeholders in CONFIG |
obsidian/home-template.md | Dashboard/Home.md | {{BRAND_LABEL}} |
obsidian/vault-overview-template.md | Dashboard/Vault-Overview.md | {{BRAND_LABEL}} |
obsidian/profile-template.md | Dashboard/{Profile}.md | {{PROFILE_NAME}} |
obsidian/setup-template.md | Dashboard/Setup.md | {{BRAND_LABEL}} |
obsidian/claude-md-template.md | Dashboard/CLAUDE.md | {{ORG_NAME}}, {{PROFILE_FOLDER_PATTERN}} |
obsidian/command-center.css | .obsidian/snippets/command-center.css | --cc-primary, --cc-canvas color vars |
obsidian/frappe-charts.min.js | Dashboard/lib/frappe-charts.min.js | none |
Bundled plugin folders under references/obsidian/plugins/ are copied verbatim into .obsidian/plugins/<id>/.
If invoked on an existing install (detect via test -f Dashboard/components/dashboard.js), offer to re-run a specific step rather than a full reinstall. Common single-step fixes: rewiring the claude binary path in .obsidian/plugins/terminal/data.json, regenerating a button's shell-command alias, or re-substituting CONFIG after a profile/page change.
Alternatives
alirezarezvani/claude-skills
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
aaron-he-zhu/aaron-marketing-skills
Use when the user asks to "build a win-back campaign", "re-engage lapsed subscribers", "run a re-permission / re-consent sweep", or "sunset my dead list"; produces a closed-loop reactivation program — a lapsed-cohort definition, a staged offer ladder, a re-consent (re-permission) capture step, and a sunset-confirm / suppression rule. Owns none of the SEND-N sub-item notes: engagement-decay / sunset is email-sequence-designer's and preference-center / frequency options is preference-frequency-man
respira-press/agent-skills-wordpress
Use when the user says 'build a design system for my site', 'extract design tokens', 'capture my brand', or 'build my style guide', or after a rebrand. Reads representative pages, theme files, and media to extract logo, colors, typography, spacing, and components, then writes a visible style-guide page.
MoizIbnYousaf/marketing-cli
Use when the user wants to generate an image or video via Higgsfield AI. Covers 30+ models: Soul V2, Seedance 2.0, Kling 3.0, Veo 3.1, GPT Image 2, Nano Banana 2. Also covers Marketing Studio — branded ad video/image with avatars and products. Use whenever: "generate an image", "make a video", "animate this photo", "image-to-video", "img2vid", "edit this image with AI", "produce a clip", "create an ad", "make a UGC video", "marketing video", "brand video", "TV spot", "import product from URL", "