* feat(config): resolve global config from an XDG-style location (#172)
The global config lived at ~/.obsidian-wiki, which isn't XDG-compliant.
It now resolves to $XDG_CONFIG_HOME/obsidian-wiki (default
~/.config/obsidian-wiki).
Existing installs are unaffected: if ~/.obsidian-wiki is present and the
XDG path is not, the legacy directory is used as-is. No migration is
required, and nothing moves on upgrade. Once the XDG path exists it wins,
so `mv ~/.obsidian-wiki ~/.config/obsidian-wiki` is all a migration takes.
Resolution is implemented identically in Python (cli.py), bash (setup.sh,
scripts/daily-update.sh, scripts/wiki-notify.sh), and the Config
Resolution Protocol that every skill defers to, so vault-scoped state and
named `config.<name>` profiles stay in one place.
* fix(config): stop setup from wiping user-added config keys
`write_config` rewrote the global config with only the three setup-managed
keys, so every `obsidian-wiki setup` — including the one a pip upgrade
prompts for — silently destroyed anything else in the file:
OBSIDIAN_LINK_FORMAT, QMD_WIKI_COLLECTION, WIKI_SKIP_PROJECTS, sync
settings, and comments.
Setup now owns only OBSIDIAN_VAULT_PATH, OBSIDIAN_WIKI_REPO and
OBSIDIAN_WIKI_VERSION, updating those in place and carrying every other
line over untouched. Duplicate definitions of a managed key collapse to
one.
Predates the XDG change (same behavior on main) but is fixed here because
this branch already touches config writing, and the XDG work makes
re-running setup more likely.
Also pins PYTHONPATH in both new test files so they exercise this
checkout rather than whatever obsidian_wiki happens to be installed.
setup.sh had an interactive git-sync flow (git init, .gitignore, remote,
a generated sync.sh, optional cron) that `obsidian-wiki setup` — the entry
point pip/uv installs actually run — never had, so those installs silently
skipped it and .env.example documented a var nothing ever read.
Move the git plumbing into obsidian_wiki/sync.py (configure_sync/run_sync/
get_remote) and expose it as `obsidian-wiki sync-setup <url>` and
`obsidian-wiki sync`. `obsidian-wiki setup` now offers the same interactive
prompt setup.sh always had (or accepts --remote non-interactively), and
setup.sh's own prompt delegates into the same module via the CLI instead of
re-implementing it. Sync status is read live from the vault's own `git
remote` (via `obsidian-wiki info`) rather than a config var that could drift.
Adds an optional GitHub sync step to setup.sh that mirrors the common
`brainsync` alias pattern but reads vault path from ~/.obsidian-wiki/config.
- Prompts during setup: repo URL, shell alias, optional hourly cron
- git-inits vault if needed, writes .gitignore for Obsidian workspace files
- Writes ~/.obsidian-wiki/sync.sh (idempotent, skips if nothing staged)
- Adds wiki-sync alias to .zshrc/.bashrc on confirmation
- Summary line shows wiki-sync if sync was configured
- README: new "Syncing your vault to GitHub" section with manual setup notes
- .env.example: documents VAULT_GITHUB_REMOTE config key
When a Hermes user runs under a named profile ($HERMES_HOME points to
~/.hermes/profiles/<name>/), setup.sh was only installing skills to
~/.hermes/skills/ (the default profile), making them invisible to the
active profile session.
Implements Option C from issue #66:
- Always installs to ~/.hermes/skills/ (default profile baseline)
- If $HERMES_HOME is set and differs from the default, also installs
to $HERMES_HOME/skills/ (active profile)
- Scans ~/.hermes/profiles/ and installs to every named profile,
skipping any already handled via $HERMES_HOME to avoid duplicates
Closes#66
Thanks for the well-structured PR — mirrors the existing `claude-history-ingest` / `codex-history-ingest` pattern cleanly and the Pi session-format details (tree JSONL with id/parentId, message roles, compaction/branch_summary) line up with pi.dev.
Reviewed:
- New `pi-history-ingest` skill, routing across `wiki-history-ingest`, `wiki-agent` (`/wiki-pi`), and `memory-bridge`
- `setup.sh`, `.env.example`, `AGENTS.md`, `README.md`, `SETUP.md`, and all four IDE rule files updated consistently
- 38 committed symlinks under `.pi/skills/` + 5 cross-agent symlinks
One small follow-up: the SKILL.md cites `references/pi-data-format.md` but the file isn't shipped (the claude/codex skills both ship that companion reference). I'll add it in a follow-up so the family stays consistent.
When stdin is closed (e.g. CI with < /dev/null), the interactive
read -p prompt returns exit code 1 (EOF). With set -e this silently
killed the script after creating .env. Adding || true lets the read
fail gracefully so setup continues with an empty vault path.
* fix(setup): use relative symlinks for in-repo skill mirrors
setup.sh has been writing absolute symlinks (e.g. /Users/<name>/obsidian-wiki/.skills/...)
into the in-repo skill mirror directories (.agents/, .claude/, .cursor/, .kiro/,
.windsurf/), even though those mirrors are tracked in git as relative symlinks
(../../.skills/<name>). The result: every fresh clone is left with ~160 modified
files immediately after running setup.sh.
This is the same class of bug that 476ac88 ("complete relative symlink migration
across all mirrors and skills") fixed in the committed tree — the migration
updated the symlinks themselves but missed the installer that recreates them.
A contributor running `git add -A` after setup would silently re-commit their
home directory into the public repo, undoing 476ac88.
Add an optional third `mode` argument to install_skills(). When "relative" is
passed, the link target is hardcoded to ../../.skills/<name> (correct for every
in-repo mirror, all of which live at $REPO/<agent-dir>/skills). The global
target dirs under $HOME continue to use absolute paths, which is what they need.
Also corrects the post-install log line — it previously said "Installed global
skills" even for in-repo dirs.
Tested: on a clean checkout, `bash setup.sh && git status` now reports a clean
tree. In-repo links read ../../.skills/<name>; global links under ~/.codex,
~/.gemini, etc. remain absolute.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* setup: harden install_skills + add CI guard
Addresses review feedback on the in-repo symlink fix:
1. Compute the relative-path prefix from target_dir's depth under
$SCRIPT_DIR instead of hardcoding ../../. The old comment claimed
"in-repo mirrors are always two levels deep" — true today, silently
wrong the moment anyone adds a deeper target. Now correct for any
depth.
2. Validate the mode arg. A typo at the call site (e.g. "rleative")
previously fell through to absolute and reintroduced the original
bug. case-statement now exits with a clear message.
3. Guard against relative mode on out-of-repo targets. Bails rather
than emitting a wrong link.
4. Assert each new symlink resolves to a SKILL.md. Catches future
path-math mistakes at install time instead of when an agent later
fails to discover skills.
5. Fold the inline ~/.claude/skills loop into install_skills via a new
optional subset argument. Removes 17 lines of duplicated
delete-and-create logic; continues the consolidation that 9377be4
started.
6. Compress install_skills docstring from 8 lines to 4.
7. Add .github/workflows/setup.yml: runs setup.sh on every PR and
fails if it modifies a tracked file. Re-runs to verify idempotency.
This is the missing guard that let the absolute-symlink regression
reach main in the first place.
Tested locally:
- bad mode → exits 1 with clear error
- relative + /tmp target → exits 1 with clear error
- subset filter installs exactly the named skills
- 4-deep test target (_test_deep/a/b/skills) gets ../../../../.skills/...
- git status clean after install; re-run is idempotent
- ~/.claude/skills contains only wiki-update + wiki-query (absolute)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Tarek Rached <tarek@kisotechnology.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the repo is cloned on Windows without `core.symlinks=true`, Git
materializes committed symlinks (under `.claude/skills/`, `.cursor/skills/`,
etc.) as regular text files whose contents are the link target path. The
`install_skills` loop and the `~/.claude/skills/` fallback only branched on
`-L` (symlink) and `-d` (directory), so a pseudo-symlink fell through to
`ln -s "$skill" "$link_path"` and failed with `File exists`. Combined with
`set -e`, the first such entry aborted the entire setup, leaving most
agents wired to dead text files and only the entries that ran before the
abort properly linked.
Add an `elif [ -f "$link_path" ]` branch in both code paths that removes
the regular file before the symlink is created. POSIX-only — no behavior
change on macOS/Linux.
Aligns obsidian-wiki's install surface with graphify's supported platforms:
- Kiro IDE/CLI: adds .kiro/steering/obsidian-wiki.md (inclusion: always) and
.kiro/skills/ as a symlink target
- Google Antigravity: adds .agent/rules/obsidian-wiki.md (alwaysApply) and
.agent/workflows/obsidian-wiki.md as the slash-command registry
- setup.sh: adds global skill symlinks for Copilot CLI (~/.copilot/skills),
Trae (~/.trae/skills), Trae CN (~/.trae-cn/skills), Kiro (~/.kiro/skills),
and the canonical Gemini CLI path (~/.gemini/skills); keeps the legacy
~/.gemini/antigravity/skills path for backwards compat
- README.md and SETUP.md: expand the Agent Compatibility matrix from 9 rows
to 17 (Gemini CLI split from Antigravity; OpenCode, Aider, Droid, Trae,
Trae CN, Kiro, Copilot CLI added) and add <details> setup blocks for each
new platform
Replaces the copied bootstrap file with a symlink so there is a single
source of truth. Also adds the hermes routing entries to AGENTS.md so all
AGENTS.md-aware agents (Codex, OpenClaw, etc.) can trigger hermes-history-ingest.
setup.sh now recreates the symlink on every run so fresh clones stay consistent.
Paths with spaces (e.g. '/LLMs Brain') were not
properly escaped or quoted, causing some LLMs to create parallel
directories. Now we:
- Quote vault paths in .env to preserve spaces during parsing
- Properly escape special characters when updating .env with sed
- Quote paths in the generated ~/.obsidian-wiki/config file
This ensures skills can find the vault regardless of spaces in the path.
The symlink installation loop (guard check → remove stale link → skip real
directories → ln -s) was copy-pasted four times across steps 2, 3b, 3c, and
3d with only the target directory varying. This makes bugs in the logic require
four separate fixes and obscures what each section is actually doing.
Extract the loop into a reusable `install_skills <target_dir> <label>` function
defined at the top of the script. Each call site is now a single line, and the
guard logic lives in exactly one place.
Changes:
- Add install_skills() function with local variables (target_dir, label,
skill_name, link_path) to avoid polluting the global shell scope
- Replace step 2's 18-line inner for-loop with a single install_skills call
- Replace steps 3b, 3c, and 3d (three identical ~15-line blocks) with three
install_skills calls
- Inline the GLOBAL_SKILLS array literal since it was only used once
- Replace `ls -d … | wc -l | tr -d ' '` skill count with a glob + grep -c
pipeline that avoids parsing ls output
Net: -47 lines (-78/+31), that is, 24% reduction.
Behavior is unchanged.
OpenClaw discovers skills from ~/.agents/skills/ (per docs.openclaw.ai/skills)
and reads AGENTS.md for project-level instructions. Both paths are now wired
into the framework:
- setup.sh adds a new global symlink step that mirrors all skills into
~/.agents/skills/, and normalizes ln -s to drop the trailing slash so
re-runs don't churn the per-agent symlink directories.
- AGENTS.md (and CLAUDE.md by mirror) document the new skill discovery paths.
- README adds an OpenClaw row to the agent compatibility table, a manual-setup
collapsible, the new global symlink in the project structure tree, and a
fifth bullet under "Using from other projects".
- Per-agent symlink directories (.claude/.cursor/.windsurf/.agents) get the
new wiki-update entry that was missing, and existing entries normalize to
the no-trailing-slash form.