feat: v1.4.0 audit fixes, multi-agent compatibility, Bases dashboard

External audit flagged 8 actionable items against current best practices for
Agent Skills, Claude Code hooks, Obsidian v1.9-v1.12, and JSON Canvas 1.0.
This release addresses all of them and adds multi-agent compatibility files
at zero cost to existing users.

Tier 1, critical fixes:

* wiki/meta/dashboard.base: NEW Obsidian Bases dashboard (native, no plugin
  needed). Replaces Dataview as the primary dashboard. Six views: Recent
  Activity, Seed Pages, Entities Missing Sources, Open Questions,
  Comparisons, Sources.
* wiki/meta/dashboard.md: now embeds dashboard.base. Legacy Dataview queries
  retained as optional fallback for users on Obsidian < 1.9.10.
* README.md and skills/wiki/references/plugins.md: Plugins section
  reorganized to recommend Bases (core, no install) primary, Dataview
  optional/legacy.
* skills/canvas/references/canvas-spec.md: added missing JSON Canvas 1.0
  fields. Group nodes now document background and backgroundStyle (cover,
  ratio, repeat). Edges document fromEnd (default 'none') and toEnd
  (default 'arrow') asymmetric defaults. Hex ID convention noted alongside
  descriptive ID alternative.
* .gitignore: track wiki/meta/dashboard.base explicitly.

Tier 2, important improvements:

* hooks/hooks.json: SessionStart now uses both command type
  ([ -f wiki/hot.md ] && cat ...) and prompt type. Command type is the
  canonical safety check that works in non-vault sessions without erroring.
  Matcher: startup|resume.
* hooks/hooks.json: NEW PostCompact hook re-injects hot cache after context
  compaction (hook-injected context does not survive compaction; CLAUDE.md
  does).
* hooks/hooks.json: PostToolUse auto-commit now guarded by [ -d .git ].
* hooks/README.md: NEW documentation including known plugin-hooks STDOUT bug
  (anthropics/claude-code#10875) and workarounds.
* skills/wiki/references/mcp-setup.md: added Option D (Obsidian CLI) for
  v1.12+. Added warning callout above NODE_TLS_REJECT_UNAUTHORIZED line
  explaining process-wide TLS bypass and recommending Option D as the
  secure alternative.
* skills/wiki-ingest/SKILL.md: documented [!contradiction] custom callout
  CSS dependency on vault-colors.css snippet.
* skills/wiki/references/css-snippets.md: full documentation of all four
  custom callouts (contradiction, gap, key-insight, stale) with built-in
  fallback equivalents.

Tier 3, multi-agent compatibility (low complexity, high reach):

* AGENTS.md: Codex CLI / OpenCode bootstrap.
* GEMINI.md: Gemini CLI / Antigravity bootstrap.
* .cursor/rules/claude-obsidian.mdc: Cursor always-on rules.
* .windsurf/rules/claude-obsidian.md: Windsurf Cascade rules.
* .github/copilot-instructions.md: GitHub Copilot conventions.
* bin/setup-multi-agent.sh: idempotent symlink installer for Codex,
  OpenCode, Gemini, Cursor, Windsurf. Wires up the skills/ directory in
  each agent's expected location.

Style cleanup: scrubbed all em dashes from every skill, hook, doc, and
bootstrap file (249 total replacements across 26 files). Skills now use
periods, commas, and colons throughout for cleaner natural prose.

Version: 1.3.0 to 1.4.0 (aligns plugin.json with GitHub release tag format).

Already resolved in v1.1 (no action needed):
* defuddle, obsidian-bases, obsidian-markdown skills shipped
* URL ingestion, vision ingestion, delta tracking docs
* Multi-depth wiki-query (Quick / Standard / Deep)
* PostToolUse auto-commit hook
* allowed-tools field removed from all SKILL.md files
* All templates already use plural tag/alias forms
* Custom callouts CSS already in vault-colors.css

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel
2026-04-08 17:41:57 +03:00
parent 3ad61b984d
commit 6bab904d13
29 changed files with 856 additions and 217 deletions

View File

@@ -3,14 +3,14 @@ name: canvas
description: "Visual layer of the wiki. Add images, text cards, PDFs, and wiki pages to Obsidian canvas files with auto-positioning inside zones. Integrates with /banana for image capture. Triggers on: /canvas, canvas new, canvas add image, canvas add text, canvas add pdf, canvas add note, canvas zone, canvas list, canvas from banana, add to canvas, put this on the canvas, open canvas, create canvas."
---
# canvas Visual Reference Layer
# canvas: Visual Reference Layer
The three knowledge capture layers:
- `/save` → text synthesis (wiki/questions/, wiki/concepts/)
- `/autoresearch` → structured knowledge (wiki/sources/, wiki/concepts/)
- `/canvas` → visual references (wiki/canvases/)
A canvas is a JSON file Obsidian renders as an infinite visual board. This skill reads and writes canvas JSON directly. Read `references/canvas-spec.md` for the full format reference before making any edits. This spec aligns with the [JSON Canvas open standard](https://jsoncanvas.org/) — if `kepano/obsidian-skills` is installed, its `json-canvas` skill is the authoritative cross-platform reference.
A canvas is a JSON file Obsidian renders as an infinite visual board. This skill reads and writes canvas JSON directly. Read `references/canvas-spec.md` for the full format reference before making any edits. This spec aligns with the [JSON Canvas open standard](https://jsoncanvas.org/). If `kepano/obsidian-skills` is installed, its `json-canvas` skill is the authoritative cross-platform reference.
---
@@ -59,7 +59,7 @@ If it does not exist, create it:
1. Slugify the name: lowercase, spaces → hyphens, strip special chars.
2. Create `wiki/canvases/[slug].canvas` with the starter structure, title updated to `# [Name]`.
3. Add entry to `wiki/overview.md` under a "## Canvases" subsection (append after the Current State section). Do not modify `wiki/index.md` — it uses a fixed section schema (Domains, Entities, Concepts, Sources, Questions, Comparisons).
3. Add entry to `wiki/overview.md` under a "## Canvases" subsection (append after the Current State section). Do not modify `wiki/index.md`. It uses a fixed section schema (Domains, Entities, Concepts, Sources, Questions, Comparisons).
4. Report: "Created wiki/canvases/[slug].canvas"
---
@@ -76,7 +76,7 @@ Create `_attachments/images/canvas/` if it doesn't exist.
**Detect aspect ratio:**
Use `python3 -c "from PIL import Image; img=Image.open('[path]'); print(img.width, img.height)"` or `identify -format '%w %h' [path]`.
See `references/canvas-spec.md` for the full aspect ratio → canvas size table (7 ratios including 4:3, 3:4, ultra-wide). Do not use an inline table here — the spec is the single source of truth for sizing.
See `references/canvas-spec.md` for the full aspect ratio → canvas size table (7 ratios including 4:3, 3:4, ultra-wide). Do not use an inline table here. The spec is the single source of truth for sizing.
**Position using auto-layout** (see Auto-Positioning section below).
@@ -117,8 +117,8 @@ Same as add image. Obsidian renders PDFs natively as file nodes.
1. Search `wiki/` for a file matching the page name (case-insensitive, partial match ok).
2. Use the vault-relative path as the `file` field.
- Use `"type": "file"` (not `"type": "link"`) `.md` files use file nodes, not link nodes.
- `"type": "link"` takes a `url: "https://..."` it is for web URLs only.
- Use `"type": "file"` (not `"type": "link"`): `.md` files use file nodes, not link nodes.
- `"type": "link"` takes a `url: "https://..."`: it is for web URLs only.
3. Create a file node: width=300, height=100.
4. Position using auto-layout.
@@ -166,8 +166,8 @@ Write and report.
3. Report:
```
wiki/canvases/main.canvas 14 nodes (8 images, 3 text, 2 file, 1 group)
wiki/canvases/design-ideas.canvas 42 nodes (30 images, 4 text, 8 groups)
wiki/canvases/main.canvas . 14 nodes (8 images, 3 text, 2 file, 1 group)
wiki/canvases/design-ideas.canvas. 42 nodes (30 images, 4 text, 8 groups)
```
---
@@ -175,7 +175,7 @@ wiki/canvases/design-ideas.canvas — 42 nodes (30 images, 4 text, 8 groups)
### from banana (`/canvas from banana`)
1. Check `wiki/canvases/.recent-images.txt` first (session log of newly written images).
2. If not found or empty: use `find` with correct precedence (parentheses required — without them `-newer` only binds to the last `-name` clause):
2. If not found or empty: use `find` with correct precedence (parentheses required. Without them `-newer` only binds to the last `-name` clause):
```bash
python3 -c "import time,os; open('/tmp/ten-min-ago','w').close(); os.utime('/tmp/ten-min-ago',(time.time()-600,time.time()-600))"
find _attachments/images -newer /tmp/ten-min-ago \( -name "*.png" -o -name "*.jpg" \)
@@ -199,7 +199,7 @@ def next_position(canvas_nodes, target_zone_label, new_w, new_h):
and n.get('label') == target_zone_label), None)
if zone is None:
# No zone place below all content
# No zone: place below all content
max_y = max((n['y'] + n.get('height', 0) for n in canvas_nodes), default=-140)
return -400, max_y + 60
@@ -223,7 +223,7 @@ def next_position(canvas_nodes, target_zone_label, new_w, new_h):
max_row_y = max(n['y'] + n.get('height', 0) for n in inside)
return zx + 20, max_row_y + 20
# Same row align to the top of all existing nodes in the zone
# Same row: align to the top of all existing nodes in the zone
current_row_y = min(n['y'] for n in inside)
return next_x, current_row_y
```
@@ -264,7 +264,7 @@ When `/banana` finishes generating images, suggest:
## Summary
1. Read canvas-spec.md before editing any canvas JSON.
2. Always read the canvas file before writing — parse existing nodes to avoid ID collisions and calculate auto-positions.
2. Always read the canvas file before writing. Parse existing nodes to avoid ID collisions and calculate auto-positions.
3. Create `_attachments/images/canvas/` for downloaded/copied images.
4. Update `wiki/index.md` when creating new canvases.
5. Report position and zone after every add operation.

View File

@@ -3,6 +3,10 @@
Canvas files are JSON with two top-level keys: `nodes` (array) and `edges` (array).
Obsidian reads and writes them as UTF-8 JSON files with `.canvas` extension.
This reference aligns with the [JSON Canvas 1.0 open specification](https://jsoncanvas.org/spec/1.0/). All structures support arbitrary additional fields (`[key: string]: any`) for forward compatibility. Obsidian will preserve unknown fields when reading and writing canvas files.
**ID format**: The JSON Canvas 1.0 spec recommends 16-character lowercase hexadecimal IDs (e.g., `"a1b2c3d4e5f67890"`). Obsidian itself generates IDs in this format. The descriptive ID examples in this reference (`"text-title-4821"`, `"img-cover-7823"`) are an alternative naming convention that this plugin uses for human readability. Both are valid JSON Canvas. Use whichever fits your workflow.
---
## Coordinate System
@@ -70,13 +74,13 @@ Renders an image, PDF, markdown note, or other vault file inline.
- Supported: `.png` `.jpg` `.webp` `.gif` `.pdf` `.md` `.canvas`
- For `.md` files: renders as a preview card.
- For `.pdf` files: renders the first page as preview.
- No `color` field for file nodes color is ignored.
- No `color` field for file nodes: color is ignored.
---
### Group node (Zone)
A labeled rectangular region. Does not clip or contain nodes — it's a visual guide.
A labeled rectangular region. Does not clip or contain nodes. It's a visual guide.
Nodes placed "inside" a group are just positioned within its bounding box.
```json
@@ -88,13 +92,20 @@ Nodes placed "inside" a group are just positioned within its bounding box.
"y": -880,
"width": 1060,
"height": 290,
"color": "6"
"color": "6",
"background": "_attachments/images/grid-bg.png",
"backgroundStyle": "cover"
}
```
- `label`: shown at the top of the group box.
- `color`: colors the group border and label.
- Groups do not affect auto-layout — they are purely visual containers.
- `background` *(optional)*: vault-relative path to a background image for the group.
- `backgroundStyle` *(optional)*: how the background is rendered.
- `"cover"`: fills the group, cropping if needed (default-ish behavior)
- `"ratio"`: preserves aspect ratio, fits inside the group
- `"repeat"`: tiles the image
- Groups do not affect auto-layout: they are purely visual containers.
---
@@ -128,6 +139,7 @@ Connections between nodes. Usually empty for mood boards.
"id": "e-hub-cidx",
"fromNode": "hub",
"fromSide": "right",
"fromEnd": "none",
"toNode": "c-idx",
"toSide": "left",
"toEnd": "arrow",
@@ -136,9 +148,16 @@ Connections between nodes. Usually empty for mood boards.
}
```
- `fromSide` / `toSide`: `"top"` `"bottom"` `"left"` `"right"`
- `toEnd`: `"arrow"` (directed) or `"none"` (undirected line)
- `label` and `color` are optional.
**Required fields**: `id`, `fromNode`, `toNode`. Everything else is optional.
- `fromNode` / `toNode`: IDs of the source and target nodes.
- `fromSide` / `toSide` *(optional)*: `"top"` `"bottom"` `"left"` `"right"`. If omitted, Obsidian auto-calculates the best side based on relative node positions.
- `fromEnd` *(optional)*: end-cap on the source side. Defaults to `"none"`. Values: `"none"` | `"arrow"`.
- `toEnd` *(optional)*: end-cap on the target side. **Defaults to `"arrow"`**: note the asymmetric default vs `fromEnd`. Values: `"none"` | `"arrow"`.
- `label` *(optional)*: text shown on the edge.
- `color` *(optional)*: same color palette as nodes (`"1"``"6"` or hex).
Most edges represent directed relationships, so the asymmetric defaults (`fromEnd: "none"`, `toEnd: "arrow"`) produce a single arrow pointing from source to target without specifying anything explicitly.
---
@@ -269,5 +288,5 @@ function place_node(canvas, zone_label, new_w, new_h):
- **Wrong path format**: use `_attachments/images/file.png` not `/home/user/...` or `~/...`
- **ID collision**: always read existing IDs before generating a new one
- **Negative y confusion**: `y: -2400` is ABOVE `y: -1000` (more negative = higher up)
- **Group does not clip**: placing a node "inside" a group is just positioning it within the group's bounding box there is no parent-child relationship in the JSON
- **Group does not clip**: placing a node "inside" a group is just positioning it within the group's bounding box: there is no parent-child relationship in the JSON
- **Missing height on text nodes**: Obsidian will render the text but may clip it if height is too small. Use height ≥ content-lines × 24.