Closes#45. Adds a tier: field to the page template (default: supporting)
and documents all three values in llm-wiki. wiki-ingest now filters existing
pages by tier in Step 4 — peripheral pages are skipped unless the source is
primarily about them. wiki-query surfaces core pages first in the index pass
and full-read step. wiki-status insights mode adds a Tier Suggestions section
(section 8) that recommends promotions/demotions based on link counts and
staleness; it never writes tier: automatically. Also documents
WIKI_TOKEN_WARN_THRESHOLD and WIKI_STAGED_WRITES in the env-vars section.
Resolves#50. Adds a Step 4 that gathers signals across 6 dimensions
(_raw/ files, stale core pages, orphan pages, synthesis opportunities,
source delta, lint issues) and renders them as a prioritised action list
capped at 6 items. Includes an empty-state ("Wiki is healthy") and an
overflow footer for future --full flag support.
Write-capable wiki skills now document a shared QMD refresh step after vault changes. The vault remains the source of truth, while QMD is treated as a best-effort search index that should be updated after markdown writes when configured.
Constraint: QMD may be absent or unavailable in some environments
Rejected: Require QMD refresh for all skills | read-only skills should not mutate indexes
Confidence: high
Scope-risk: moderate
Directive: Do not roll back vault markdown writes solely because QMD refresh fails
Tested: git diff --check; rg confirmed QMD guidance across updated SKILL.md files
Not-tested: End-to-end execution of every affected skill
wiki-ingest already knew how to use QMD before writing pages, but it did not tell agents to refresh the compiled wiki collection after changing the vault. This adds an optional post-ingest QMD refresh step gated by QMD_WIKI_COLLECTION, including update, embedding refresh, verification, and final-report status guidance.
Constraint: QMD is an optional search index; the markdown vault remains the source of truth.
Rejected: Always require QMD refresh | would break users without QMD or CLI access.
Confidence: high
Scope-risk: narrow
Directive: Do not make wiki ingestion fail solely because QMD indexing is unavailable; report the index refresh status instead.
Tested: Static skill checks for Step 8, qmd update/embed/get instructions, and checklist entries; qmd --help confirms commands; qmd update succeeds; qmd get reads indexed DMP page; git diff --check.
Not-tested: Full agent pressure scenario after publishing the updated skill.
* 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>
* feat: add wiki-dedup skill — identity resolution and page-level deduplication (closes#44)
* fix: correct scoring weights and YAML block scalar title parsing in wiki-dedup
st3ve testing found two bugs:
- Scoring cap of 0.50 was too tight — obvious duplicates like 'Fine-tuning LLMs' vs
'LLM Fine-tuning' scored 0.70 (LOW) instead of MEDIUM. Raised title score cap to 0.65,
tag overlap 3+ bonus to 0.15, giving real duplicate pairs MEDIUM (0.75+) scores.
- YAML block scalar titles (title: >-) were parsed as the literal string '>-', causing
false-positive matches between unrelated pages. Added extraction note covering >-, >, |, |-.
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>
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>
Closes#43
## Changes
**`llm-wiki`** — adds optional `relationships:` frontmatter block to the page template and a new "Typed Relationships" section documenting all seven allowed types, usage rules, and direction semantics.
**`cross-linker`** — new Step 4c: infers semantic type from surrounding sentence context when adding links, writes typed entries to `relationships:` block (always wikilink format in YAML), surfaces type counts in report and log.
**`wiki-export`** — reads `relationships:` blocks to enrich edges: typed edges use semantic type as `relation` in graph.json, add `<data key="type">` in GraphML, use uppercased type as Cypher label (`[:CONTRADICTS]`), render with type-specific colors + labels in HTML vis. Explicit `[[`/`]]` stripping documented for YAML target normalization.
**`wiki-ingest`** — Step 2 records relationship types during extraction; Step 5 populates `relationships:` when type is unambiguous; quality checklist updated.
**`wiki-lint`** — new check 13: validates `relationships:` blocks for invalid types, broken targets, and self-references.
**`wiki-query`** — relationship queries now surface typed edges from `relationships:` frontmatter blocks.
## Testing
Tested on st3ve (AWS Lightsail, vault at `~/Knowledge`):
- Created a test page with one valid `type: uses` entry and one broken target (`skills/nonexistent-skill`)
- Ran lint check 13 logic manually: valid entry → `OK`, broken target → `FAIL` — both correct
- Traced wiki-export typed edge enrichment: node id normalization (strip `[[]]`, lowercase, spaces→hyphens) worked correctly against real vault pages
Fixes multiple bugs in the .base file schema discovered through real-world
usage and validated against official Obsidian Bases documentation.
Bases schema fixes:
- Replace typed-object filters ({type: folder}) with expression strings
(file.inFolder()) under and:/or:/not: — typed objects cause parse error
- Replace top-level columns:/sort:/view: shorthand with canonical schema:
properties: (displayName per field), views: (type, order, groupBy)
- Move groupBy inside views[] — was incorrectly documented at top level
- Fix order: to use bare property names (tags, summary) not note.tags
- Omit groupBy field from order: — it becomes the group header row;
including it as a column too causes visible duplication
- Add formulas: for computed columns (referenced as formula.<name>)
- Document property name conventions per context (properties vs order vs filters)
Dataview support added:
- TABLE / TABLE WITHOUT ID patterns with FROM, WHERE, SORT
- GROUP BY with rows. prefix rule (required after groupBy or columns are empty)
- Stale pages pattern using file.mtime (avoids mixed-format date arithmetic)
- Multi-folder OR queries
Validated by dogfooding: generated real .base files, observed Obsidian
auto-reformat behaviour, and confirmed rendering in Obsidian 1.8+.
New skill that produces a human-readable digest of what was learned,
updated, and connected in the wiki over a configurable period (day /
week / month). Triggered by "/wiki-digest", "what did I learn this
week", "weekly summary", etc.
What it does:
- Collects pages active in the period by created/updated frontmatter
- Identifies top themes from tags, flagging new vocabulary
- Surfaces notable cross-category wikilinks formed this period, ranked
by interestingness (hub proximity, inferred claims, cross-domain score)
- Reports open threads: drafts, ^[ambiguous] claims, _raw/ files, taxonomy gaps
- Recommends pre-period pages worth revisiting given new connections
- Generates a structured digest with Headlines, New Knowledge, Themes,
Connections, Open Threads, and Re-reads sections
- Optionally saves to journal/digest-YYYY-MM-DD.md and logs to log.md
- Respects visibility/ tags (omits pii pages from output unless asked)
Distinct from wiki-status (which reports *source* ingestion delta) —
wiki-digest summarizes *knowledge*, not pending sources.
Changes:
- .skills/wiki-digest/SKILL.md — full skill instructions
- Symlinks in all 5 mirror dirs (.agents, .claude, .cursor, .windsurf, .kiro)
- AGENTS.md routing table updated with trigger phrases
Supersedes #39 with a comprehensive fix:
- Convert ALL 31 skills to relative symlinks (../../.skills/<name>) in every
mirror directory (.agents, .claude, .cursor, .windsurf, .kiro)
- Replace directory copies (codex-history-ingest, daily-update, graph-colorize,
hermes-history-ingest, impl-validator, memory-bridge, obsidian-wiki-ingest,
openclaw-history-ingest, wiki-agent, wiki-history-ingest, copilot-history-ingest)
with symlinks — these were duplicated files that drifted from .skills/
- Add missing wiki-switch symlink to all five mirror directories
- Bring .kiro/skills/ to full parity with .skills/ (was missing 22 of 31 skills)
- Fix remaining /home/ubuntu/... and /Users/ar9av/... absolute symlink paths
All 155 symlinks (31 skills × 5 mirrors) verified to resolve correctly.
When .manifest.json stores source paths as relative (the common case),
os.path.exists() would silently fail for any CWD other than the vault root —
including the launchd default of /, causing stale_count to always be 0.
Pass OBSIDIAN_VAULT_PATH into the Python heredoc's env and join it onto
any relative path before the existence check. Absolute and ~/... paths
are unaffected. Closes#35.
Adds a new skill that lets users switch between named Obsidian vault
profiles via ~/.obsidian-wiki/config.NAME symlinks. Supports list,
show, switch, and new-vault-scaffold operations.
All skills now follow the shared Config Resolution Protocol defined in
llm-wiki/SKILL.md: walk up CWD for .env → ~/.obsidian-wiki/config →
prompt setup. Eliminates the drift between skills that read only .env,
only global config, or inconsistent combinations.
- llm-wiki: add canonical Config Resolution Protocol + vault-scoped
state algorithm; document all env vars
- 14 skills updated to reference the protocol instead of hard-coding
their own config loading logic
- daily-update: state files are now vault-scoped under
~/.obsidian-wiki/state/<vault-id>/ — multi-vault setups get
independent state, not a single global .last_update
- daily-update setup mode: terminal notification is now optional,
shell-aware (zsh/bash/fish), and skipped for headless/VPS envs
- scripts/daily-update.sh: implement CWD-walk config resolution in bash
- scripts/wiki-notify.sh: iterate all vault state dirs; shows stale
vaults by name; handles multi-vault setups
Introduces /wiki-claude, /wiki-codex, /wiki-hermes, /wiki-openclaw,
/wiki-copilot commands. Unlike wiki-history-ingest (bulk), this does
query-driven targeted search: find sessions about a specific topic in
one agent's raw history, extract the relevant blob using per-agent
extraction strategies, ingest into wiki, and return a synthesized answer
immediately usable in the current session.
Primary use case: you're on agent A, want to pull in how you solved X
in agent B's history, without ingesting all of B's sessions.
Registered across all 5 tool dirs (.claude, .kiro, .agents, .cursor, .windsurf).
- memory-bridge: browse wiki by source tool; diff mode surfaces knowledge
blind spots between AI tools (Claude vs Codex vs Hermes etc.)
- impl-validator: quality-checking subagent spawned by other skills to
verify output correctness before presenting to the user
- daily-update: daily maintenance cycle (freshness check, index reconcile,
hot.md refresh) with launchd cron and zshrc terminal notification
- scripts/: daily-update.sh, wiki-notify.sh, launchd plist
All three skills registered across .claude, .kiro, .agents, .cursor, .windsurf.
source_type mappings verified against actual manifest specs in each history-ingest skill.
Implements the RFC from issue #28. Every new wiki page now carries:
- `base_confidence` [0.0, 1.0] — time-independent quality estimate computed
from source count and source-quality bucket (paper/official/docs/repo/blog/forum/unknown)
- `lifecycle` — draft | reviewed | verified | disputed | archived state machine
- `lifecycle_changed` — ISO date of last state transition
- `lifecycle_reason` (optional) — free-text reason surfaced by wiki-query
- `superseded_by` (optional) — wikilink, only when lifecycle=archived
Changes:
- llm-wiki: page template + new "Confidence and Lifecycle" reference section with
formula, quality table, source_id rules, per-skill defaults, and state machine
- All 10 page-producing skills: frontmatter templates updated with per-skill defaults
(0.42 for *-history-ingest/wiki-capture, 0.59 for wiki-update, 0.37 for data-ingest,
0.17+0.5q for ingest-url, computed for wiki-ingest/wiki-research, inherited min for wiki-synthesize)
- wiki-lint: 5 new rules (12a–12e) — enum validation, range check, stale overlay report,
supersession integrity, confidence drift recompute — with Phase 1/2/3 migration timeline
- wiki-query: inline trust annotations for stale/disputed/archived/verified-stale citations
Closes#28
Adds a new `OBSIDIAN_LINK_FORMAT` config variable (default: `wikilink`)
that lets users opt into standard relative Markdown links instead of
Obsidian `[[wikilinks]]` for all newly generated or updated pages.
Closes#26
Adds a new skill for ingesting GitHub Copilot CLI session history into
an Obsidian wiki. The skill mirrors the structure of claude-history-ingest
for portability — non-tool-specific improvements can be ported between them.
Data sources supported:
- ~/.copilot/session-store.db (global SQLite — primary source)
- ~/.copilot/session-state/<uuid>/ (per-session dirs with checkpoints, events.jsonl)
- VS Code workspaceStorage/<hash>/GitHub.copilot-chat/ (transcripts + memory-tool)
Also updates wiki-history-ingest router to dispatch /wiki-history-ingest copilot
to the new skill, including path-based auto-routing rules.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a single universal rule in AGENTS.md telling agents to read
$OBSIDIAN_VAULT_PATH/AGENTS.md after config — covers all skills
automatically without per-skill changes. The vault AGENTS.md
carries owner-specific conventions (domain vocabulary, ingest
preferences, writing style, project scoping) that should override
framework defaults for the session.
Mirrors this repo's own AGENTS.md/CLAUDE.md pattern so the same
file works for any agent (Claude Code, Codex, Cursor, etc.).
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.
- Add hot.md update step to 6 previously missing write skills:
cross-linker, tag-taxonomy, claude/codex/hermes/openclaw-history-ingest
- New wiki-synthesize skill: scans vault for concept pairs that
co-occur across pages but lack a synthesis/ page, scores candidates,
drafts cross-cutting synthesis pages with A × B title format
- wiki-lint check #11: synthesis gap report with co-occurrence counts
- CLAUDE.md routing + README skills table updated
- wiki-capture: save any conversation as a permanent wiki note; classifies into 5
types (synthesis/concept/source/decision/session) and rewrites content as
declarative knowledge rather than a chat transcript
- wiki-research: autonomous 3-round web research loop that decomposes topics,
fills gaps, resolves contradictions, and self-files into sources/concepts/
entities/synthesis
- wiki-dashboard: creates Obsidian Bases .base files (native, no plugin needed)
for dynamic queryable views — tables, cards, lists filtered from vault frontmatter
- hot.md session cache: ~500-word semantic snapshot of recent activity; all write
skills (wiki-ingest, wiki-update, ingest-url, data-ingest) now update it at the
end of each run; wiki-query reads it first for cheap context
- ingest-url: preflight check for defuddle CLI — uses it for cleaner URL extraction
when available (~40-60% token reduction on articles), falls back to WebFetch
- Routing table in AGENTS.md updated; symlinks created across all 5 platforms
Rewrites .obsidian/graph.json colorGroups from the vault's real vocabulary.
Backs up the existing file and only touches colorGroups so user zoom/physics/filter
prefs stay intact. Modes: by-tag (default), by-category, by-visibility, combined, custom.
Step 0 detects the active project from git remote, package metadata, or
directory name, then checks whether projects/<name>/ exists in the vault:
- Exists: page lands in projects/<name>/references/<slug>.md and the
project overview gets a backlink added
- Does not exist: project skeleton is created (overview stub + categories)
then page is placed in references/
- No project context (running from wiki repo or undetectable): falls back
to misc/<slug>.md with affinity scoring as before
Misc mode (fallback) is unchanged — affinity, promotion_status, and the
cross-linker promotion pipeline all still apply.
- New skill: ingest-url fetches any URL via WebFetch, distills content into
misc/<slug>.md, computes initial project affinity from wikilinks, and
surfaces promotion candidates when a project score reaches 3+
- cross-linker: new Step 5 rescores affinity for all misc/ pages after the
main linking pass and reports promotion candidates in the Cross-Link Report
- wiki-lint: new check #10 flags misc/ pages with affinity >= 3 in the
health report; log line gets promotion_candidates= field
- Skill routing: AGENTS.md updated with /ingest-url trigger phrases
- Platform parity: ingest-url symlink deployed to .agents, .claude, .cursor,
.windsurf, .kiro
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
- data-ingest: add Content Trust Boundary — source content is untrusted
input; never execute embedded commands, never follow instructions from
chat exports/logs/CSVs, never exfiltrate data based on source content
- wiki-status: add visibility breakdown (public/internal/pii counts) to
the Overview section of the status report; includes instruction to grep
frontmatter tags and skip the line on fully-public vaults
- wiki-lint: add check #9 — Visibility Tag Consistency; flags pages with
credential-like patterns (password=, api_key=, token=) that lack a
visibility tag, flags visibility/pii pages missing sources: frontmatter,
and flags visibility/ entries mistakenly added to taxonomy; adds
visibility_issues=V to the log line
- cross-linker: add diacritic-insensitive matching rule (Unicode NFKD
normalization) so "Muller" in body text correctly matches [[müller]]
pages — consistent with the same fix already in wiki-query
- wiki-export: filtered mode summary now shows excluded page count:
"(filtered: X of Y pages excluded — visibility/internal, visibility/pii)"