diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index 2d8f91e..c7cfb8c 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -21,7 +21,9 @@ guidance) with this file (guidance for editing this repo). module), `provision.sh` (standalone-runnable, sources `lib/provision-lib.sh`), optional `requires`. A module whose whole directory is linked (`tmux`, `kitty`) keeps its config under `config/` so - the manifest and provisioning don't land in `$HOME`. + the manifest and provisioning don't land in `$HOME`. A hand-run module + (`obsidian`) has neither file: it ships a script the owner runs directly, + plus a README, and no role lists it. - `modules/x11/` - the bspwm + sxhkd + polybar stack plus picom, dunst, redshift, Xresources, the systemd user unit, and the X11-only scripts. Grouped because sxhkd's hotkeys and polybar's toggle scripts reference diff --git a/README.md b/README.md index 0b1e88f..3185eb5 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ roles/ server, workstation, desktop - one module name per line modules// install.conf.yaml (dotbot links, sources relative to the module) provision.sh (pinned + sha256-verified tooling; standalone-runnable) requires (optional: modules this one cannot work without) + a hand-run module (obsidian) instead ships a script and + README, and no role lists it hosts/ per-machine values (see below) apps/ applications that live here but are not dotfiles lib/ provision-lib.sh (pins, sha256, apt), roles.sh, common.sh, @@ -70,6 +72,10 @@ Modules: `desktop-session-log`. These stay together because sxhkd's hotkeys and polybar's toggle scripts reference bspwm's installed paths directly. - `wacom` - one tablet's `xsetwacom` mapping; per-host. +- `obsidian` - a starter pack for new Obsidian vaults (settings, vimrc, + folder layout, `AGENTS.md`, `Home.md`). A hand-run module: no role lists + it; run `modules/obsidian/seed-vault `. Seeded vaults own + their copy afterwards. See [the module README](modules/obsidian/README.md). Applications: diff --git a/modules/agents/README.md b/modules/agents/README.md index d669152..ef9faef 100644 --- a/modules/agents/README.md +++ b/modules/agents/README.md @@ -38,7 +38,10 @@ dotbot/bin/dotbot -d "$PWD/modules/agents" -c modules/agents/install.conf.yaml - ## Project convention Keep shared authored context under `/.agents/`. A project's root -`AGENTS.md` and `CLAUDE.md` point to `.agents/AGENTS.md`. A distinct Claude +`AGENTS.md` and `CLAUDE.md` point to `.agents/AGENTS.md`. Claude Code reads +`AGENTS.md` when a directory has no `CLAUDE.md`, so a project with only a +root `AGENTS.md` (such as a vault from `modules/obsidian`) needs no +`CLAUDE.md`; its Claude skills still go in `.claude/skills/`. A distinct Claude instruction document can live at `.agents/CLAUDE.md` with its existing entry point retained. Nested instruction scopes use the same layout in that scope. Project skill sources live at `.agents/skills/`; Claude gets individual @@ -68,14 +71,3 @@ This installer has no required dependency on those files or on a notes vault. `vault` replaces the former local `homebase` user skill. Its general vault workflow lives here; application-specific Homebase procedures belong to that application's project skills. The old global `homebase` links are retired. - -`generate-notes`, `improve-notes`, `optimize`, and `tiling-tree` were migrated -from `krishna-bala/claude-bala-plugins` at commit -`ddc2c451db7542104b288db680c760c7a08a14ce`. The first three lived under -`pkm/skills/`; `tiling-tree` lived under `skills/`. Their initial contents are -preserved, including Claude-specific optional frontmatter and existing workflow -references. The source repository retains their earlier Git history. The owner -kept these four available while considering pruning or redesign. - -The PKM workflows still assume a separate notes vault at `~/notes`; installing -them does not create that vault or its optional Anki integrations. diff --git a/modules/agents/install.conf.yaml b/modules/agents/install.conf.yaml index 90cd060..8ae6ce1 100644 --- a/modules/agents/install.conf.yaml +++ b/modules/agents/install.conf.yaml @@ -16,11 +16,3 @@ # Link individual skills so other installed skills remain independent. ~/.agents/skills/vault: skills/vault ~/.claude/skills/vault: skills/vault - ~/.agents/skills/generate-notes: skills/generate-notes - ~/.claude/skills/generate-notes: skills/generate-notes - ~/.agents/skills/improve-notes: skills/improve-notes - ~/.claude/skills/improve-notes: skills/improve-notes - ~/.agents/skills/optimize: skills/optimize - ~/.claude/skills/optimize: skills/optimize - ~/.agents/skills/tiling-tree: skills/tiling-tree - ~/.claude/skills/tiling-tree: skills/tiling-tree diff --git a/modules/agents/skills/generate-notes/SKILL.md b/modules/agents/skills/generate-notes/SKILL.md deleted file mode 100644 index 35c5279..0000000 --- a/modules/agents/skills/generate-notes/SKILL.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: generate-notes -user-invocable: true -disable-model-invocation: false -description: > - Creates vault notes from a source. Triggers on: "generate notes", "take notes on", - "create notes from", "summarize this into notes", "notes from this URL", - "notes from this paper", "notes from this chapter" -allowed-tools: WebFetch, Read, Write(~/notes/*), Bash(curl *), Bash(mkdir *), Glob, Grep ---- - -# Generate Notes - -Creates structured vault notes from a source (URL, file, folder, or pasted text). No Anki interaction. - -## Arguments - -`` is a URL, file path, folder path, or the user pastes text directly. - -## Procedure - -### 1. Load conventions - -- Read `~/notes/CLAUDE.md` for vault-wide conventions (LaTeX, linking, callouts, frontmatter) -- If the target directory is known, read its CLAUDE.md too (e.g., `~/notes/projects/courses/underactuated/CLAUDE.md`) - -### 2. Acquire source content - -- URL: `WebFetch(url, "Extract key concepts, definitions, equations in LaTeX, and section structure")`. If WebFetch fails, ask user to paste content. -- File/PDF: `Read(path)` (for PDFs, use `pages` parameter for large ones) -- Folder: `Glob` for files, then `Read` each -- Pasted text: use directly - -### 3. Triage large sources - -If source is large (>10k words estimated): -- Summarize the structure and sections found -- Ask user which sections to prioritize -- Process in priority order - -### 4. Determine target location - -If not obvious from context: -- Analyze source type (course chapter? standalone concept? reference?) -- Ask user where notes should live -- Suggest based on vault structure conventions - -### 5. Extract images (URL sources only) - -- Fetch raw HTML: `Bash(curl -s )` -- `Grep` for image URLs (`src="...svg|jpg|png"`) -- Download relevant images: `Bash(curl -s -o ~/notes//figures/ )` -- Create figures directory if needed: `Bash(mkdir -p ~/notes//figures/)` -- If extraction fails, continue without images and note the gap - -### 6. Create notes - -Write note(s) at the appropriate vault path with: -- Proper frontmatter per directory CLAUDE.md conventions -- LaTeX using `$...$` and `$$...$$` (vault format, NOT Anki format) -- `\mathbf{x}` for bold (NOT `\bold{x}`) -- Embedded figures: `![[figures/filename.svg]]` -- Wikilinks to existing concepts where applicable -- Callout blocks for formal definitions, theorems, examples per vault conventions - -### 7. Report - -- Note path(s) created -- Sections covered -- Images stored (if any) -- Suggest running `/generate-cards` if the notes contain card-worthy material diff --git a/modules/agents/skills/improve-notes/SKILL.md b/modules/agents/skills/improve-notes/SKILL.md deleted file mode 100644 index 9be9ae2..0000000 --- a/modules/agents/skills/improve-notes/SKILL.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -name: improve-notes -user-invocable: true -disable-model-invocation: false -description: > - Improves vault note quality based on feedback or analysis. Triggers on: - "improve notes", "fix notes", "update notes", "notes need work", - "refine notes", "clean up notes" -allowed-tools: Read, Write(~/notes/*), Edit, Glob, Grep ---- - -# Improve Notes - -Improves vault note quality based on review feedback, user input, or structural analysis. No Anki interaction. - -## Arguments - -Optional `[scope]`: note path, topic/course name, or omitted for broad analysis. - -## Procedure - -### 1. Load conventions - -- Read `~/notes/CLAUDE.md` for vault-wide conventions -- Read the target directory CLAUDE.md for structure-specific conventions - -### 2. Resolve scope - -- Specific note path: read it directly -- Topic/course name: read `~/notes/.claude/deck-map.yaml` to resolve vault path, then `Glob` for notes in that directory -- Omitted: ask user for target, or scan a suggested directory - -### 3. Check for flagged issues - -- If `anki-review-feedback.md` exists in the resolved directory, check for entries flagged as note-level issues by `/improve-cards` -- These are prioritized (the card was fine, but the source note needs updating) - -### 4. Analyze notes - -Scan for: -- Outdated or incomplete content (stub sections, TODO markers) -- Broken wikilinks (`Grep` for `[[...]]` patterns, verify targets exist via `Glob`) -- LaTeX issues (`\bold{x}` instead of `\mathbf{x}`, malformed equations) -- Missing or incorrect frontmatter per directory conventions -- Concept extraction candidates (mature subsections that could become standalone concept notes) -- Inconsistency with Anki cards (if feedback file mentions drift between note and card content) - -### 5. Present findings - -- Prioritized list with note path, issue description, and proposed fix -- Feedback-flagged issues first, then structural issues -- Cap at 20 findings - -### 6. Execute approved fixes - -User approves by item or category: -- Content fixes: `Edit` for targeted changes, `Write` only for new files (concept extractions) -- Frontmatter fixes: `Edit` to add/correct fields -- Broken links: fix or remove -- Concept extraction: create new note in `concepts/`, add wikilink from source - -### 7. Report - -- Notes modified -- New concept notes extracted (if any) -- If note changes affect existing Anki cards, suggest running `/generate-cards` to update or `/improve-cards` to fix diff --git a/modules/agents/skills/optimize/SKILL.md b/modules/agents/skills/optimize/SKILL.md deleted file mode 100644 index aaf8abf..0000000 --- a/modules/agents/skills/optimize/SKILL.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -name: optimize -user-invocable: true -disable-model-invocation: false -description: > - Vault-wide audit and housekeeping. Triggers on: "optimize vault", "audit", - "housekeeping", "check vault", "vault health", "vault audit", - "optimize notes", "clean up vault" -allowed-tools: Read, Glob, Grep, Write(~/notes/*), Edit, Bash(ls *) ---- - -# Optimize - -Vault-wide audit and housekeeping. Checks structural conventions, frontmatter completeness, and content staleness. - -## Arguments - -Optional `[scope]`: `all`, `projects`, `concepts`, `references`, `daily`. Default: `all`. - -## Procedure - -### 1. Load all vault conventions - -Read CLAUDE.md files: -- `~/notes/CLAUDE.md` (vault root) -- `~/notes/concepts/CLAUDE.md` -- `~/notes/projects/CLAUDE.md` -- `~/notes/projects/courses/CLAUDE.md` -- `~/notes/references/CLAUDE.md` -- `~/notes/daily/CLAUDE.md` -- `~/notes/templates/CLAUDE.md` - -### 2. Structural audit - -- Every project subfolder has an `index.md` with `aliases: [folder-name]` in frontmatter -- Files are in the correct directory for their `type` frontmatter value -- No orphan files in vault root (outside of known top-level files) -- Wikilinks in index files resolve to existing notes - -### 3. Frontmatter audit - -Check required fields per type: -- Project files: `type`, `status`, `description`, `tags` -- Concept files: `status`, `tags` -- Reference files: `type`, `tags` -- Daily notes: `date` -- Valid status values: `active`, `paused`, `backlog`, `complete`, `ongoing` (projects); `seedling`, `budding`, `evergreen` (concepts) - -### 4. Staleness audit - -Using frontmatter `created` date: -- Seedling concepts >60 days old with <100 chars content (beyond frontmatter) -- Active projects >90 days old with only stub content -- Empty files (<50 chars beyond frontmatter) -- Missing `created` field: flag as frontmatter issue -- Never flag `evergreen` or `complete` status as stale - -### 5. Report - -- Summary count: "Found N issues: X critical, Y warnings, Z info" -- Severity-grouped table (critical first) -- Cap at 20 issues per run -- Severity guide: - - Critical: missing index.md, wrong directory for type, broken required frontmatter - - Warning: missing optional frontmatter, stale content, orphan files - - Info: concept extraction candidates, minor inconsistencies - -### 6. Execute approved fixes - -User approves by category or individually: -- Missing indices: create from template with proper frontmatter -- Missing frontmatter fields: add with sensible defaults -- Misplaced files: suggest move, confirm before executing -- Never modify note content (only metadata and structure) - -### 7. Report changes - -- Files created, modified, or moved -- Issues resolved vs remaining diff --git a/modules/agents/skills/tiling-tree/SKILL.md b/modules/agents/skills/tiling-tree/SKILL.md deleted file mode 100644 index c18cacd..0000000 --- a/modules/agents/skills/tiling-tree/SKILL.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -name: tiling-tree -user-invocable: true -disable-model-invocation: false -description: > - Interactive tiling tree method for systematically mapping solution spaces. - Decomposes a problem into MECE (mutually exclusive, collectively exhaustive) - branches with low branching factor. Use when exploring solution spaces, - brainstorming approaches, evaluating architectural alternatives, or needing - comprehensive coverage of options. Also known as morphological analysis - (Fritz Zwicky). Triggers on: "tiling tree", "solution space", "map out approaches", - "morphological analysis", "MECE", "all possible ways to", "systematic brainstorm", - "explore alternatives" -argument-hint: "[problem or solution space to explore]" ---- - -# Tiling Tree: $ARGUMENTS - -Systematically map the solution space by constructing a tiling tree, a recursive MECE decomposition that forces comprehensive exploration of all possible approaches. - -A tiling tree takes the complete set of possible solutions to a problem and splits it into non-overlapping subsets that together cover the entire original set. Each subset is split further, recursively, until reaching concrete actionable ideas. The method's value comes from forcing you to think about branches you'd normally skip. - -## Interaction approach - -Work through the tree interactively with the user, one level at a time. Do not generate the whole tree unprompted. The user's thinking at each split is the point, not the final artifact. - -### Phase 1: Define the root - -Ask the user to precisely define the solution space: - -- "What problem are you trying to solve? State it as specifically as you can." -- "What constraints bound the solution space? What's definitely out of scope?" -- "What does a successful solution look like?" - -Push for precise language. The root definition determines everything downstream. If the user says "improve performance," ask: whose performance? measured how? under what conditions? - -Write out the agreed root definition explicitly before proceeding. - -### Phase 2: Find the first split - -Guide the user to the most natural top-level decomposition: - -- "What's the most fundamental dimension along which solutions to this problem differ?" -- "If you had to sort every possible approach into a few buckets, what would those buckets be?" - -**Branching factor guidance:** -- Aim for 3-5 branches per node. This maps well to how humans naturally categorize and reason about trade-offs. -- Binary splits ("has X" / "doesn't have X") are allowed when genuinely natural, but don't default to them. They often create an artificial "everything else" bucket that hides interesting structure. -- More than 5 branches usually means two split dimensions are mixed together. Split one dimension first, then the other at the next level down. - -**What makes a good split:** -- It exposes a meaningful trade-off (cost vs. capability, simplicity vs. flexibility, etc.) -- The branches are grounded in something durable: physical principles, mathematical properties, or fundamental mechanisms -- Each branch suggests a genuinely different class of approach, not just a parameter variation - -**MECE check (do this at every split):** -- Mutually exclusive: "Could a solution plausibly belong to two of these branches?" If yes, rework the split. -- Collectively exhaustive: "Can you think of a valid approach that doesn't fit any branch?" If yes, a branch is missing. - -### Phase 3: Recursive decomposition - -For each branch at the current level: - -1. Define it precisely: what's included, what's excluded -2. Ask: "Can this branch be split further into a few natural sub-categories?" -3. Apply the same MECE checks and split quality criteria -4. Stop splitting when you reach: - - Concrete, actionable approaches (leaf nodes) - - Branches that are clearly infeasible (mark why) - - Branches that need investigation before splitting further - -**Work breadth-first**, completing one full level before going deeper. This prevents tunnel vision on a single branch and helps spot cross-branch patterns. - -**Force exploration on every branch**, especially ones the user wants to dismiss quickly. Ask: "Before we mark this as infeasible, what would have to be true for this approach to work?" Dead-end branches with documented reasoning are valuable. They prevent revisiting the same dead end later, and may become viable when circumstances change. - -### Phase 4: Evaluate leaves - -For each leaf node, assign a status: - -- `[promising]` — Worth pursuing. Note key advantages and risks. -- `[dead end]` — Infeasible or clearly dominated. Note the specific reason so it can be revisited if conditions change. -- `[needs research]` — Can't evaluate without more information. Note what specifically needs investigation. -- `[exists]` — Already implemented or well-known. Note the reference. - -### Phase 5: Reflect - -After completing the tree: - -- "Which branches surprised you? What approaches hadn't you considered?" -- "Are there alternative ways to split at the top level that might reveal different solutions?" -- "Which 'needs research' branches have the highest potential upside?" - -Different top-level split dimensions provoke genuinely different ideas. Offer to construct a second tree from a different angle if the user wants to explore further. - -## Output format - -Present the completed tree as structured text with clear hierarchy: - -``` -Root: [precise problem definition] - -Split by: [dimension name] -├── Branch A: [precise definition] -│ Split by: [sub-dimension] -│ ├── A.1: [description] [promising] — key advantage -│ ├── A.2: [description] [dead end] — reason -│ └── A.3: [description] [needs research] — what to investigate -├── Branch B: [precise definition] -│ ├── B.1: [description] [promising] — key advantage -│ └── B.2: [description] [exists] — reference -└── Branch C: [precise definition] - └── (needs further decomposition) -``` - -Label each split dimension explicitly. This makes it easy to see when dimensions have been mixed at the same level. - -## Facilitation principles - -- **One level at a time**: The user's reasoning at each split matters more than the finished tree. -- **Precision over speed**: Spend time getting definitions right. Sloppy labels cause MECE violations and hide gaps. -- **Challenge completeness relentlessly**: "What's missing?" is the most valuable question in this method. Novel ideas emerge from gaps you're forced to fill. -- **Dead ends are documentation**: A branch marked infeasible with a clear reason is valuable work, not wasted effort. -- **Trees are snapshots, not verdicts**: Branches dismissed today may become viable as technology, costs, or knowledge change. Note assumptions that could shift. diff --git a/modules/obsidian/README.md b/modules/obsidian/README.md new file mode 100644 index 0000000..4533c62 --- /dev/null +++ b/modules/obsidian/README.md @@ -0,0 +1,61 @@ +# obsidian + +A starter pack for a new Obsidian vault: settings (Catppuccin appearance, +hotkeys, vimrc, CSS snippet, per-plugin settings), a folder layout, a root +`AGENTS.md`, `Home.md`, and a `.gitignore`. After seeding, each vault owns +its copy and is free to drift from this one. + +The module is in no role. `./install` and `./provision.sh` skip it; run the +script by hand. + +## Seed a vault + +```sh +~/.dotfiles/modules/obsidian/seed-vault ~/my-vault +``` + +The script copies files without overwriting existing ones, creates the +folders, and runs `git init` with no remote. Re-running it only fills in +what is missing. + +## Finish in Obsidian + +These steps are manual, or for an agent to walk through with the owner. + +1. Open the folder as a vault ("Open folder as vault"). +2. Settings → Community plugins: turn off Restricted mode, then Browse + and install each of these by name. The seeded settings are picked up + on install. + + | Search for | Plugin id | + |----------------|---------------------------| + | Templater | `templater-obsidian` | + | Dataview | `dataview` | + | Style Settings | `obsidian-style-settings` | + | Vimrc Support | `obsidian-vimrc-support` | + | Date Inserter | `date-inserter` | + | Datepicker | `datepicker` | + | Mermaid Tools | `mermaid-tools` | + | TikZJax | `obsidian-tikzjax` | + +3. Settings → Appearance → Themes → Manage: install Catppuccin. +4. Restart Obsidian so the vimrc and plugins load. + +## Fonts + +`appearance.json` asks for Google Sans Flex (text and interface) and +Google Sans Code (monospace). Both are OFL-licensed families on Google +Fonts; neither ships with Linux distributions. Without them Obsidian falls +back to its bundled Inter and the system monospace font, so this step is +cosmetic. + +To install on Linux, download both families from fonts.google.com, then: + +```sh +mkdir -p ~/.local/share/fonts/google-sans +unzip -o '' -d ~/.local/share/fonts/google-sans +fc-cache -f +fc-list | grep -i 'google sans' # both families should be listed +``` + +Restart Obsidian afterwards. diff --git a/modules/obsidian/config/app.json b/modules/obsidian/config/app.json new file mode 100644 index 0000000..eed9033 --- /dev/null +++ b/modules/obsidian/config/app.json @@ -0,0 +1,58 @@ +{ + "alwaysUpdateLinks": true, + "vimMode": true, + "foldIndent": true, + "foldHeading": true, + "showLineNumber": false, + "promptDelete": false, + "readableLineLength": false, + "pdfExportSettings": { + "includeName": true, + "pageSize": "Letter", + "landscape": false, + "margin": "0", + "downscalePercent": 100 + }, + "tabSize": 2, + "legacyEditor": false, + "livePreview": true, + "showFrontmatter": false, + "showInlineTitle": false, + "rightToLeft": false, + "spellcheck": false, + "defaultViewMode": "source", + "showUnsupportedFiles": true, + "attachmentFolderPath": "attachments", + "mobileToolbarCommands": [ + "command-palette:open", + "editor:toggle-checklist-status", + "editor:toggle-bullet-list", + "editor:toggle-numbered-list", + "editor:indent-list", + "editor:unindent-list", + "editor:undo", + "editor:set-heading", + "editor:toggle-bold", + "editor:toggle-italics", + "editor:toggle-strikethrough", + "editor:insert-wikilink", + "editor:insert-tag", + "editor:attach-file", + "editor:toggle-highlight", + "editor:toggle-code", + "editor:toggle-blockquote", + "editor:insert-link", + "editor:insert-embed", + "editor:redo", + "editor:toggle-keyboard", + "templater-obsidian:insert-templater", + "editor:configure-toolbar", + "editor:swap-line-down", + "editor:swap-line-up" + ], + "propertiesInDocument": "hidden", + "newFileLocation": "folder", + "newFileFolderPath": "_inbox", + "openBehavior": "file:Home.md", + "useTab": false +} diff --git a/modules/obsidian/config/appearance.json b/modules/obsidian/config/appearance.json new file mode 100644 index 0000000..9dbfde3 --- /dev/null +++ b/modules/obsidian/config/appearance.json @@ -0,0 +1,15 @@ +{ + "accentColor": "", + "theme": "obsidian", + "cssTheme": "Catppuccin", + "textFontFamily": "Google Sans Flex,Inter", + "interfaceFontFamily": "Google Sans Flex,Inter", + "baseFontSize": 17, + "baseFontSizeAction": true, + "showRibbon": false, + "showViewHeader": true, + "monospaceFontFamily": "Google Sans Code,Menlo,Source Code Pro", + "enabledCssSnippets": [ + "line-spacing" + ] +} \ No newline at end of file diff --git a/modules/obsidian/config/community-plugins.json b/modules/obsidian/config/community-plugins.json new file mode 100644 index 0000000..21f6039 --- /dev/null +++ b/modules/obsidian/config/community-plugins.json @@ -0,0 +1,10 @@ +[ + "obsidian-style-settings", + "templater-obsidian", + "obsidian-vimrc-support", + "date-inserter", + "datepicker", + "dataview", + "mermaid-tools", + "obsidian-tikzjax" +] diff --git a/modules/obsidian/config/core-plugins.json b/modules/obsidian/config/core-plugins.json new file mode 100644 index 0000000..b90dd4a --- /dev/null +++ b/modules/obsidian/config/core-plugins.json @@ -0,0 +1,34 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": true, + "page-preview": true, + "daily-notes": false, + "templates": false, + "note-composer": false, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": true, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false, + "webviewer": false, + "starred": true, + "footnotes": false, + "bases": true +} \ No newline at end of file diff --git a/modules/obsidian/config/hotkeys.json b/modules/obsidian/config/hotkeys.json new file mode 100644 index 0000000..7603401 --- /dev/null +++ b/modules/obsidian/config/hotkeys.json @@ -0,0 +1,167 @@ +{ + "app:go-back": [ + { + "modifiers": [ + "Alt" + ], + "key": "ArrowLeft" + } + ], + "app:go-forward": [ + { + "modifiers": [ + "Alt" + ], + "key": "ArrowRight" + } + ], + "editor:toggle-code": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "C" + } + ], + "backlink:toggle-backlinks-in-document": [ + { + "modifiers": [ + "Alt" + ], + "key": "B" + } + ], + "app:open-vault": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "," + } + ], + "editor:toggle-source": [ + { + "modifiers": [ + "Alt" + ], + "key": "E" + } + ], + "editor:toggle-numbered-list": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "8" + } + ], + "templater-obsidian:insert-templater": [ + { + "modifiers": [ + "Alt" + ], + "key": "O" + } + ], + "templater-obsidian:create-new-note-from-template": [ + { + "modifiers": [ + "Alt", + "Mod" + ], + "key": "O" + } + ], + "editor:toggle-bullet-list": [ + { + "modifiers": [ + "Mod" + ], + "key": "." + } + ], + "editor:cycle-list-checklist": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "." + } + ], + "file-explorer:new-file": [ + { + "modifiers": [ + "Mod" + ], + "key": "N" + } + ], + "app:toggle-left-sidebar": [ + { + "modifiers": [ + "Alt" + ], + "key": "[" + } + ], + "app:toggle-right-sidebar": [ + { + "modifiers": [ + "Alt" + ], + "key": "]" + } + ], + "editor:open-link-in-new-leaf": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "Enter" + } + ], + "editor:follow-link": [ + { + "modifiers": [ + "Mod" + ], + "key": "Enter" + } + ], + "switcher:open": [], + "editor:delete-paragraph": [], + "editor:toggle-italics": [], + "editor:toggle-bold": [], + "app:reload": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "R" + } + ], + "date-inserter:open-calendar": [ + { + "modifiers": [ + "Alt", + "Mod" + ], + "key": "H" + } + ], + "file-explorer:reveal-active-file": [ + { + "modifiers": [ + "Alt", + "Mod" + ], + "key": "[" + } + ] +} diff --git a/modules/obsidian/config/plugins/dataview/data.json b/modules/obsidian/config/plugins/dataview/data.json new file mode 100644 index 0000000..7863d8c --- /dev/null +++ b/modules/obsidian/config/plugins/dataview/data.json @@ -0,0 +1,27 @@ +{ + "renderNullAs": "\\-", + "taskCompletionTracking": true, + "taskCompletionUseEmojiShorthand": true, + "taskCompletionText": "completion", + "taskCompletionDateFormat": "yyyy-MM-dd", + "recursiveSubTaskCompletion": false, + "warnOnEmptyResult": true, + "refreshEnabled": true, + "refreshInterval": 2500, + "defaultDateFormat": "yyyy-MM-dd", + "defaultDateTimeFormat": "yyyy-MM-dd HH:mm", + "maxRecursiveRenderDepth": 4, + "tableIdColumnName": "File", + "tableGroupColumnName": "Group", + "showResultCount": true, + "allowHtml": true, + "inlineQueryPrefix": "=", + "inlineJsQueryPrefix": "$=", + "inlineQueriesInCodeblocks": true, + "enableInlineDataview": true, + "enableDataviewJs": false, + "enableInlineDataviewJs": false, + "prettyRenderInlineFields": true, + "prettyRenderInlineFieldsInLivePreview": true, + "dataviewJsKeyword": "dataviewjs" +} diff --git a/modules/obsidian/config/plugins/date-inserter/data.json b/modules/obsidian/config/plugins/date-inserter/data.json new file mode 100644 index 0000000..48b6acb --- /dev/null +++ b/modules/obsidian/config/plugins/date-inserter/data.json @@ -0,0 +1,17 @@ +{ + "dateFormatSpecs": [ + { + "format": "yyyy-mm-dd", + "regex": "\\d{4}-\\d{2}-\\d{2}", + "minLength": 10, + "maxLength": 10 + } + ], + "defaultDate": "today", + "format": "yyyy-mm-dd", + "format2": "", + "language": "en", + "weekStart": 0, + "todayHighlight": true, + "daysOfWeekHighlighted": {} +} diff --git a/modules/obsidian/config/plugins/datepicker/data.json b/modules/obsidian/config/plugins/datepicker/data.json new file mode 100644 index 0000000..eab863f --- /dev/null +++ b/modules/obsidian/config/plugins/datepicker/data.json @@ -0,0 +1,14 @@ +{ + "dateFormat": "YYYY-MM-DD", + "overrideFormat": false, + "showDateButtons": false, + "showTimeButtons": false, + "showAutomatically": false, + "autoApplyEdits": true, + "immediatelyShowCalendar": true, + "autofocus": false, + "focusOnArrowDown": false, + "insertIn24HourFormat": false, + "selectDateText": false, + "ignoreCodeblocks": false +} diff --git a/modules/obsidian/config/plugins/obsidian-style-settings/data.json b/modules/obsidian/config/plugins/obsidian-style-settings/data.json new file mode 100644 index 0000000..02edc95 --- /dev/null +++ b/modules/obsidian/config/plugins/obsidian-style-settings/data.json @@ -0,0 +1,10 @@ +{ + "catppuccin-theme-settings@@catppuccin-theme-dark": "ctp-mocha", + "catppuccin-theme-settings@@catppuccin-theme-accents": "ctp-full-palette", + "catppuccin-icon-styles@@ctp-icon-hide": false, + "catppuccin-interface-styles@@ctp-bold-folder-title": true, + "catppuccin-font-styles@@ctp-italic": "var(--ctp-accent)", + "catppuccin-tag-styles@@ctp-tag-pill": true, + "catppuccin-font-styles@@ctp-bold": "var(--ctp-lavender)", + "catppuccin-font-styles@@ctp-strikethrough": "var(--ctp-subtext0)" +} diff --git a/modules/obsidian/config/plugins/obsidian-vimrc-support/data.json b/modules/obsidian/config/plugins/obsidian-vimrc-support/data.json new file mode 100644 index 0000000..f5a8aac --- /dev/null +++ b/modules/obsidian/config/plugins/obsidian-vimrc-support/data.json @@ -0,0 +1,14 @@ +{ + "vimrcFileName": ".obsidian.vimrc", + "displayChord": false, + "displayVimMode": false, + "fixedNormalModeLayout": false, + "capturedKeyboardMap": {}, + "supportJsCommands": false, + "vimStatusPromptMap": { + "normal": "🟢", + "insert": "🟠", + "visual": "🟡", + "replace": "🔴" + } +} \ No newline at end of file diff --git a/modules/obsidian/config/plugins/templater-obsidian/data.json b/modules/obsidian/config/plugins/templater-obsidian/data.json new file mode 100644 index 0000000..ef9b181 --- /dev/null +++ b/modules/obsidian/config/plugins/templater-obsidian/data.json @@ -0,0 +1,34 @@ +{ + "command_timeout": 5, + "templates_folder": "templates", + "templates_pairs": [ + [ + "", + "" + ] + ], + "trigger_on_file_creation": true, + "auto_jump_to_cursor": false, + "enable_system_commands": false, + "shell_path": "", + "user_scripts_folder": "", + "enable_folder_templates": true, + "folder_templates": [], + "enable_file_templates": false, + "file_templates": [ + { + "regex": ".*", + "template": "" + } + ], + "syntax_highlighting": true, + "syntax_highlighting_mobile": false, + "enabled_templates_hotkeys": [ + "" + ], + "startup_templates": [ + "" + ], + "intellisense_render": 1, + "enable_ribbon_icon": true +} \ No newline at end of file diff --git a/modules/obsidian/config/snippets/line-spacing.css b/modules/obsidian/config/snippets/line-spacing.css new file mode 100644 index 0000000..7a58ba8 --- /dev/null +++ b/modules/obsidian/config/snippets/line-spacing.css @@ -0,0 +1,39 @@ +/* Line spacing adjustment */ +body { + --line-height-normal: 1.7; /* default is 1.5, increase for more space */ +} + +/* Extra spacing between list items */ +/* Reading view */ +.markdown-rendered li { + margin-bottom: 6px !important; +} +.markdown-rendered :is(ul, ol) { + margin-top: 8px !important; +} +/* Live Preview */ +.cm-line.HyperMD-list-line { + padding-bottom: 6px !important; +} +.cm-line:not(.HyperMD-list-line) + .cm-line.HyperMD-list-line { + padding-top: 8px !important; +} + +.cm-line.HyperMD-header { + padding-bottom: 6px !important; +} + +/* Readable line length width (only applies if setting is enabled) */ +body { + --file-line-width: 900px; /* default is ~700px */ +} + +/* Center note content at 80% of the available pane width. */ +@media (min-width: 800px) { + body:not(.is-mobile) .markdown-source-view.mod-cm6 .cm-sizer, + body:not(.is-mobile) .markdown-preview-view .markdown-preview-sizer { + width: 80%; + max-width: 80%; + margin-inline: auto; + } +} diff --git a/modules/obsidian/config/vault/AGENTS.md b/modules/obsidian/config/vault/AGENTS.md new file mode 100644 index 0000000..815da67 --- /dev/null +++ b/modules/obsidian/config/vault/AGENTS.md @@ -0,0 +1,56 @@ +# Vault + +Obsidian knowledge vault. Start navigation at [[Home]]. + +## Storage + +- The vault is local to this machine. Git records local history; the repo starts with no remote, and adding one is the owner's decision. +- `.obsidian/` settings are committed alongside the notes. `workspace*.json`, `.env*`, and local agent settings are ignored. + +## Structure + +``` +concepts/ # Ideas you synthesized: flat, evergreen, reusable +projects/ # Things you work on, including their meetings and logs +references/ # External information: people, teams, systems, processes + people/ +archive/ # Finished or historical material +templates/ # Templater templates +attachments/ # Pasted images and files +_inbox/ # Default location for new notes; sort regularly +.claude/ # Claude Code skills, hooks, and settings for this vault +``` + +- Boundary test: "Did I author this insight?" → `concepts/`. "Am I recording external info?" → `references/`. "Am I working on it?" → `projects/`. +- Folders describe domain, not status. Completed projects stay in `projects/` with `status: complete`. +- New notes land in `_inbox/`. Move a note to its folder once its home is clear; `Home.md` lists what is waiting. +- Add an `AGENTS.md` to a folder when it gains conventions of its own. This file is authoritative at the root; folder files add local rules. + +## Properties (frontmatter) + +- Universal: `type`, `created` (`YYYY-MM-DD`), `tags`. +- Projects: `type: project`, `status` (`active`, `paused`, `backlog`, `complete`, `ongoing`). `Home.md` lists every note with `type: project` that is not `complete`, regardless of folder. +- Index files serve as MOCs with dataview queries and include `aliases: []` so they can be linked as `[[folder-name]]`. + +## Writing conventions + +- Write each prose paragraph on a single source line and let Obsidian wrap it. Preserve Markdown structure for lists, tables, code blocks, and headings. +- No H1 headings; the filename is the title. +- Dates are `YYYY-MM-DD` everywhere. +- Links: `[[Entity]]` for wikilinks, `[[Note#Section]]` for sections, `[text]` in single brackets for visual distinction only (ticket numbers, tool names), `#tag` for categories and states. +- Callouts: use Obsidian's built-in types (`> [!note]`, `> [!example]`, `> [!tip]`, `> [!warning]`, `> [!question]`). +- Math: `$...$` inline, `$$...$$` display. Use `\mathbf{x}` (not `\bold`). +- Distinguish decisions, proposals, and open questions. Record who decided and when. + +## Templates + +Templater reads `templates/`. Templates have frontmatter and no H1; use `<% tp.date.now("YYYY-MM-DD") %>` for dates. When an agent creates a note from a template, it writes the expanded values (such as today's date) instead of the `<% %>` tags. + +## Keys + +- `Ctrl+P` opens the command palette. +- Vim bindings come from the Vimrc Support plugin and `.obsidian.vimrc` at the vault root: `jk`/`jj` escape, `` quick switcher, `fg` search in files, `e` left sidebar, `` split navigation, `gd` follow link. + +## Agents + +Agents read this `AGENTS.md`; no `CLAUDE.md` is needed. Put Claude Code specific skills, hooks, and settings under `.claude/` (`.claude/skills//SKILL.md`, `.claude/settings.json`); `.claude/settings.local.json` and `.claude/plans/` stay untracked. diff --git a/modules/obsidian/config/vault/Home.md b/modules/obsidian/config/vault/Home.md new file mode 100644 index 0000000..35388d4 --- /dev/null +++ b/modules/obsidian/config/vault/Home.md @@ -0,0 +1,31 @@ +--- +type: index +tags: [vault-organization] +--- + +## Projects + +```dataview +TABLE status, created +WHERE type = "project" AND status != "complete" +SORT status ASC, file.mtime DESC +``` + +## Inbox + +New notes land in `_inbox/`. Move each one to its folder once it has a home. + +```dataview +LIST +FROM "_inbox" +SORT file.ctime DESC +``` + +## Recently edited + +```dataview +LIST +WHERE file.name != this.file.name +SORT file.mtime DESC +LIMIT 10 +``` diff --git a/modules/obsidian/config/vault/gitignore b/modules/obsidian/config/vault/gitignore new file mode 100644 index 0000000..267cec4 --- /dev/null +++ b/modules/obsidian/config/vault/gitignore @@ -0,0 +1,4 @@ +.obsidian/workspace*.json +.env* +**/.claude/settings.local.json +**/.claude/plans/ diff --git a/modules/obsidian/config/vimrc b/modules/obsidian/config/vimrc new file mode 100644 index 0000000..a0e40a6 --- /dev/null +++ b/modules/obsidian/config/vimrc @@ -0,0 +1,178 @@ +" Obsidian Vimrc - LazyVim-style bindings +" Requires: Vimrc Support plugin (esm) + +" ============================================ +" LEADER KEY +" ============================================ +unmap + +" ============================================ +" ESCAPE ALTERNATIVES +" ============================================ +imap jk +imap jj + +" ============================================ +" FOLDING (zc/zo/za) +" ============================================ +exmap foldmore obcommand editor:fold-more +exmap foldless obcommand editor:fold-less +exmap togglefold obcommand editor:toggle-fold +exmap foldall obcommand editor:fold-all +exmap unfoldall obcommand editor:unfold-all + +nnoremap zc :foldmore +nnoremap zo :foldless +nnoremap za :togglefold +nnoremap zM :foldall +nnoremap zR :unfoldall + +" ============================================ +" NAVIGATION - VISUAL LINES (like LazyVim) +" ============================================ +" j/k use native movement (handles folds correctly) +" gj/gk skips folded headers in Obsidian's CodeMirror vim mode + +" ============================================ +" NAVIGATION - CENTER AFTER JUMPS +" ============================================ +nmap n nzzzv +nmap N Nzzzv +" Half-page scroll (workaround - native C-d may not work) +nnoremap 15jzz +nnoremap 15kzz +nmap G Gzz + +" ============================================ +" YANK / PASTE IMPROVEMENTS +" ============================================ +" Y yanks to end of line (consistent with D, C) +nmap Y y$ + +" ============================================ +" SEARCH +" ============================================ +nmap h :nohl + +" ============================================ +" FILE OPERATIONS (f...) +" ============================================ +exmap openSwitcher obcommand switcher:open +exmap searchInFiles obcommand global-search:open + +nmap ff :openSwitcher +nmap fg :searchInFiles + +" ============================================ +" QUICK SWITCHER () +" ============================================ +" Find or create note (was Ctrl+O) +nmap :openSwitcher + +" ============================================ +" WINDOW/SPLIT NAVIGATION +" ============================================ +exmap focusLeft obcommand editor:focus-left +exmap focusRight obcommand editor:focus-right +exmap focusTop obcommand editor:focus-top +exmap focusBottom obcommand editor:focus-bottom + +nmap :focusLeft +nmap :focusRight +nmap :focusTop +nmap :focusBottom + +" Split commands +exmap splitVertical obcommand workspace:split-vertical +exmap splitHorizontal obcommand workspace:split-horizontal +exmap closePane obcommand workspace:close + +nmap wv :splitVertical +nmap ws :splitHorizontal +nmap wq :closePane + +" ============================================ +" SIDEBAR TOGGLES (e, b) +" ============================================ +exmap toggleLeft obcommand app:toggle-left-sidebar +exmap toggleRight obcommand app:toggle-right-sidebar + +nmap e :toggleLeft +nmap b :toggleRight + +" ============================================ +" NOTES (n...) +" ============================================ +exmap newNote obcommand file-explorer:new-file + +nmap nn :newNote + +" ============================================ +" GO TO / JUMP LIST (g... or g...) +" ============================================ +exmap goBack obcommand app:go-back +exmap goForward obcommand app:go-forward +exmap followLink obcommand editor:follow-link + +" Jump list navigation (prev/next) +nmap :goBack +nmap :goForward + +" Follow link under cursor +nmap gd :followLink +nmap gf :followLink + +" ============================================ +" SURROUND (wiki links, quotes, etc.) +" ============================================ +exmap surroundWiki surround [[ ]] +exmap surroundDoubleQuotes surround " " +exmap surroundSingleQuotes surround ' ' +exmap surroundBackticks surround ` ` +exmap surroundParens surround ( ) +exmap surroundBrackets surround [ ] +exmap surroundBraces surround { } +exmap surroundUnderscores surround _ _ +exmap surroundAsterisks surround * * +exmap surroundBold surround ** ** + +" Use in visual mode: select text, then press the key +vmap [[ :surroundWiki +vmap S" :surroundDoubleQuotes +vmap S' :surroundSingleQuotes +vmap S` :surroundBackticks +vmap S( :surroundParens +vmap S[ :surroundBrackets +vmap S{ :surroundBraces +vmap _ :surroundUnderscores +vmap * :surroundAsterisks +vmap ` :surroundBackticks +" Bold: Ctrl+B, Italic: Ctrl+I +vmap :surroundBold +vmap :surroundUnderscores + +" ============================================ +" EDITING +" ============================================ +exmap cycleCheckbox obcommand editor:cycle-list-checklist +exmap toggleBold obcommand editor:toggle-bold +exmap toggleItalic obcommand editor:toggle-italics + +nmap x :cycleCheckbox +" Normal mode: toggle bold/italic on word under cursor +nmap :toggleBold +nmap :toggleItalic + +" ============================================ +" DATE INSERTER +" ============================================ +exmap openDateCalendar obcommand date-inserter:open-calendar +nmap :openDateCalendar +imap :openDateCalendar + +" ============================================ +" MISC +" ============================================ +exmap openSettings obcommand app:open-settings + +nmap , :openSettings diff --git a/modules/obsidian/seed-vault b/modules/obsidian/seed-vault new file mode 100755 index 0000000..ca5f114 --- /dev/null +++ b/modules/obsidian/seed-vault @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# +# seed-vault - lay down a new Obsidian vault from the starter pack in +# config/: the folder layout, .obsidian settings (theme, hotkeys, snippets, +# plugin settings), the vimrc, a starter AGENTS.md and Home.md, and a git +# repo with no remote. +# +# usage: seed-vault +# +# Run by hand, once per vault; no role lists this module, so ./install and +# ./provision.sh never touch it. Files are copied rather than linked because +# Obsidian rewrites its JSON whenever a setting changes, and a vault's tweaks +# should stay in that vault. An existing file is never overwritten, so a +# re-run only fills in what is missing. +# +# Plugin code, the Catppuccin theme, and fonts are not shipped here; see +# README.md for the steps that finish the vault inside Obsidian. + +set -euo pipefail + +MODULE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../../lib/common.sh +. "$MODULE_DIR/../../lib/common.sh" + +CONFIG="$MODULE_DIR/config" +[ $# -eq 1 ] || die "usage: seed-vault " +VAULT="$1" + +# usage: place - copy unless dest already exists. +place() { + if [ -e "$2" ]; then + skip "${2#"$VAULT"/} exists" + else + mkdir -p "$(dirname "$2")" + cp "$1" "$2" + note "added ${2#"$VAULT"/}" + fi +} + +log "seeding $VAULT" +mkdir -p "$VAULT" +VAULT="$(cd "$VAULT" && pwd)" + +for d in concepts projects references/people archive templates attachments _inbox .claude/skills; do + mkdir -p "$VAULT/$d" +done + +# Obsidian settings: everything under config/ except the vimrc (which lives +# at the vault root) and vault/ (starter content). +while IFS= read -r -d '' f; do + rel="${f#"$CONFIG"/}" + place "$f" "$VAULT/.obsidian/$rel" +done < <(find "$CONFIG" -path "$CONFIG/vault" -prune -o -type f ! -name vimrc -print0) +place "$CONFIG/vimrc" "$VAULT/.obsidian.vimrc" + +# Starter content. gitignore is stored without its dot so it does not apply +# to this repo. +while IFS= read -r -d '' f; do + rel="${f#"$CONFIG/vault"/}" + [ "$rel" = gitignore ] && rel=.gitignore + place "$f" "$VAULT/$rel" +done < <(find "$CONFIG/vault" -type f -print0) + +if [ -d "$VAULT/.git" ]; then + skip "git repo exists" +else + git -C "$VAULT" init -q + note "initialised git repo (no remote)" +fi +if [ -n "$(git -C "$VAULT" remote)" ]; then + note "this vault already has a git remote: $(git -C "$VAULT" remote | paste -sd' ')" +fi + +log "Next: finish the vault in Obsidian ($MODULE_DIR/README.md)"