add claude-obsidian
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-dragonscale.sh — opt-in installer for DragonScale Memory.
|
||||
#
|
||||
# Provisions the runtime files that the wiki-ingest and wiki-lint skills
|
||||
# feature-detect. Safe to re-run (idempotent).
|
||||
#
|
||||
# Does NOT install ollama or pull any embedding model. Those are
|
||||
# prerequisites for Mechanism 3 (semantic tiling) and are the user's
|
||||
# responsibility. Mechanism 1 (fold) and Mechanism 2 (addresses) have no
|
||||
# external prerequisites.
|
||||
#
|
||||
# Usage:
|
||||
# bash bin/setup-dragonscale.sh [optional: /path/to/vault]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VAULT="${1:-$(dirname "$SCRIPT_DIR")}"
|
||||
|
||||
echo "Setting up DragonScale Memory at: $VAULT"
|
||||
cd "$VAULT"
|
||||
|
||||
# ── 1. Verify required artifacts that ship with the plugin ───────────────────
|
||||
for required in "scripts/allocate-address.sh" "scripts/tiling-check.py" "skills/wiki-fold/SKILL.md"; do
|
||||
if [ ! -e "$required" ]; then
|
||||
echo "ERR: missing $required. Reinstall the claude-obsidian plugin." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
chmod +x scripts/allocate-address.sh scripts/tiling-check.py
|
||||
|
||||
# ── 2. Provision .vault-meta/ ─────────────────────────────────────────────────
|
||||
mkdir -p .vault-meta
|
||||
if [ ! -f .vault-meta/address-counter.txt ]; then
|
||||
echo "1" > .vault-meta/address-counter.txt
|
||||
echo "OK .vault-meta/address-counter.txt initialized at 1"
|
||||
else
|
||||
echo "-- .vault-meta/address-counter.txt already present (not overwritten)"
|
||||
fi
|
||||
|
||||
if [ ! -f .vault-meta/tiling-thresholds.json ]; then
|
||||
cat > .vault-meta/tiling-thresholds.json <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"model": "nomic-embed-text",
|
||||
"bands": {
|
||||
"error": 0.90,
|
||||
"review": 0.80
|
||||
},
|
||||
"calibrated": false,
|
||||
"calibration_pairs_labeled": 0,
|
||||
"notes": "Conservative seed thresholds, NOT calibrated against this vault. See skills/wiki-lint/SKILL.md Semantic Tiling section for the calibration procedure."
|
||||
}
|
||||
JSON
|
||||
echo "OK .vault-meta/tiling-thresholds.json initialized with conservative seed bands"
|
||||
else
|
||||
echo "-- .vault-meta/tiling-thresholds.json already present (not overwritten)"
|
||||
fi
|
||||
|
||||
# ── 3. Provision .raw/.manifest.json (if absent) ──────────────────────────────
|
||||
mkdir -p .raw
|
||||
if [ ! -f .raw/.manifest.json ]; then
|
||||
cat > .raw/.manifest.json <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"created": "DRAGONSCALE_SETUP",
|
||||
"description": "Ingest delta tracker and address map for the claude-obsidian vault. Do not hand-edit; wiki-ingest maintains this.",
|
||||
"sources": {},
|
||||
"address_map": {}
|
||||
}
|
||||
JSON
|
||||
# Replace placeholder with today's date
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
sed -i.bak "s/DRAGONSCALE_SETUP/$DATE/" .raw/.manifest.json
|
||||
rm -f .raw/.manifest.json.bak
|
||||
echo "OK .raw/.manifest.json initialized (empty sources + address_map)"
|
||||
else
|
||||
echo "-- .raw/.manifest.json already present (not overwritten)"
|
||||
fi
|
||||
|
||||
# ── 4. Rollout-baseline marker in legacy-pages.txt ────────────────────────────
|
||||
if [ ! -f .vault-meta/legacy-pages.txt ]; then
|
||||
cat > .vault-meta/legacy-pages.txt <<EOF
|
||||
# DragonScale legacy-pages manifest
|
||||
# rollout: $(date +%Y-%m-%d)
|
||||
#
|
||||
# List, one path per line, any pages whose frontmatter \`created:\` date is
|
||||
# post-rollout but which should still be treated as legacy (i.e. not required
|
||||
# to have an address). Also lines beginning with "# rollout:" set the
|
||||
# per-vault rollout baseline used by wiki-lint for severity classification.
|
||||
# Example:
|
||||
# wiki/sources/old-page-with-wrong-metadata.md
|
||||
EOF
|
||||
echo "OK .vault-meta/legacy-pages.txt initialized (rollout baseline set to today)"
|
||||
else
|
||||
echo "-- .vault-meta/legacy-pages.txt already present (not overwritten)"
|
||||
fi
|
||||
|
||||
# ── 5. Sanity checks ──────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "Sanity checks:"
|
||||
NEXT=$(./scripts/allocate-address.sh --peek 2>&1 | tail -1)
|
||||
echo " next address: c-$(printf '%06d' $NEXT)"
|
||||
|
||||
PYTHON=$(command -v python3 || echo "not installed")
|
||||
echo " python3: $PYTHON"
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if curl -sS --max-time 2 http://localhost:11434/api/version >/dev/null 2>&1; then
|
||||
echo " ollama: reachable at http://localhost:11434"
|
||||
if curl -sS --max-time 2 http://localhost:11434/api/tags | grep -q nomic-embed-text; then
|
||||
echo " nomic-embed: installed"
|
||||
else
|
||||
echo " nomic-embed: NOT installed (run 'ollama pull nomic-embed-text' to enable Mechanism 3)"
|
||||
fi
|
||||
else
|
||||
echo " ollama: not reachable (Mechanism 3 will no-op; install from https://ollama.com)"
|
||||
fi
|
||||
else
|
||||
echo " curl: not installed (cannot check ollama)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "DragonScale setup complete."
|
||||
echo "See wiki/concepts/DragonScale Memory.md for the full spec."
|
||||
echo "See skills/wiki-fold/ for Mechanism 1 (log folds)."
|
||||
echo "wiki-ingest and wiki-lint will now feature-detect DragonScale automatically."
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-mode.sh — interactive methodology mode selector (v1.8+).
|
||||
#
|
||||
# Sets the vault's .vault-meta/mode.json and optionally seeds template
|
||||
# folders for the chosen mode. Idempotent — safe to re-run to switch modes.
|
||||
# Existing files are NOT auto-migrated; the new mode only affects future
|
||||
# filing operations.
|
||||
#
|
||||
# Usage:
|
||||
# bash bin/setup-mode.sh # interactive
|
||||
# bash bin/setup-mode.sh --mode lyt # non-interactive (CI / scripts)
|
||||
# bash bin/setup-mode.sh --mode generic --no-seed
|
||||
# bash bin/setup-mode.sh --check # diagnostics only, no write
|
||||
#
|
||||
# Flags:
|
||||
# --mode MODE Skip the interactive prompt; pick MODE directly.
|
||||
# Valid: generic | lyt | para | zettelkasten
|
||||
# --no-seed Skip the optional folder-seeding step
|
||||
# --check Print current mode + diagnostics; write nothing
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — success
|
||||
# 2 — usage error
|
||||
# 3 — invalid mode string
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VAULT="$(dirname "$SCRIPT_DIR")"
|
||||
WM="$VAULT/scripts/wiki-mode.py"
|
||||
|
||||
REQUESTED_MODE=""
|
||||
NO_SEED=false
|
||||
CHECK_ONLY=false
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--mode) REQUESTED_MODE="${2:-}"; shift 2 ;;
|
||||
--no-seed) NO_SEED=true; shift ;;
|
||||
--check) CHECK_ONLY=true; shift ;;
|
||||
-h|--help)
|
||||
sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
;;
|
||||
*) echo "ERR: unknown flag: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() { printf '%s\n' "$@"; }
|
||||
warn() { printf 'WARN: %s\n' "$@" >&2; }
|
||||
|
||||
say "═══ wiki-mode setup (v1.8+) ═══"
|
||||
say "Vault: $VAULT"
|
||||
say ""
|
||||
|
||||
# ── Sanity check ────────────────────────────────────────────────────────────
|
||||
if [ ! -x "$WM" ]; then
|
||||
warn "scripts/wiki-mode.py not found or not executable: $WM"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# ── Diagnostics ─────────────────────────────────────────────────────────────
|
||||
CURRENT=$(python3 "$WM" get 2>/dev/null || echo "generic")
|
||||
say "Current mode: $CURRENT"
|
||||
|
||||
if $CHECK_ONLY; then
|
||||
say ""
|
||||
python3 "$WM" config
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Mode selection ──────────────────────────────────────────────────────────
|
||||
if [ -z "$REQUESTED_MODE" ]; then
|
||||
say ""
|
||||
say "Pick a methodology mode for this vault:"
|
||||
say " 1) generic — v1.7 default; wiki/sources/, entities/, concepts/"
|
||||
say " 2) lyt — Linking Your Thinking (MOCs + atomic notes flat under wiki/notes/)"
|
||||
say " 3) para — Projects / Areas / Resources / Archives"
|
||||
say " 4) zettelkasten — timestamped IDs, flat under wiki/, dense linking"
|
||||
say ""
|
||||
printf "Pick [1-4, default 1]: "
|
||||
read -r choice || choice="1"
|
||||
case "${choice:-1}" in
|
||||
1|generic) REQUESTED_MODE="generic" ;;
|
||||
2|lyt) REQUESTED_MODE="lyt" ;;
|
||||
3|para) REQUESTED_MODE="para" ;;
|
||||
4|zettelkasten) REQUESTED_MODE="zettelkasten" ;;
|
||||
*) warn "invalid choice: $choice"; exit 3 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
case "$REQUESTED_MODE" in
|
||||
generic|lyt|para|zettelkasten) ;;
|
||||
*) warn "invalid mode: $REQUESTED_MODE (valid: generic|lyt|para|zettelkasten)"; exit 3 ;;
|
||||
esac
|
||||
|
||||
# ── Write the mode ──────────────────────────────────────────────────────────
|
||||
python3 "$WM" set "$REQUESTED_MODE"
|
||||
|
||||
# ── Seed template folders (optional) ────────────────────────────────────────
|
||||
if ! $NO_SEED; then
|
||||
if [ -t 0 ]; then
|
||||
say ""
|
||||
printf "Seed template folders for %s? [y/N]: " "$REQUESTED_MODE"
|
||||
read -r seed || seed="n"
|
||||
else
|
||||
seed="n"
|
||||
fi
|
||||
case "${seed:-n}" in
|
||||
[yY]|[yY][eE][sS])
|
||||
case "$REQUESTED_MODE" in
|
||||
lyt)
|
||||
mkdir -p "$VAULT/wiki/mocs" "$VAULT/wiki/notes"
|
||||
say "✓ Created wiki/mocs/ and wiki/notes/"
|
||||
;;
|
||||
para)
|
||||
mkdir -p "$VAULT/wiki/projects/inbox" "$VAULT/wiki/areas" \
|
||||
"$VAULT/wiki/resources/incoming" "$VAULT/wiki/resources/people" \
|
||||
"$VAULT/wiki/resources/concepts" "$VAULT/wiki/archives"
|
||||
say "✓ Created PARA folder structure: projects/{inbox}/, areas/, resources/{incoming,people,concepts}/, archives/"
|
||||
;;
|
||||
zettelkasten)
|
||||
say "✓ Zettelkasten uses no subfolders; all notes file flat under wiki/"
|
||||
;;
|
||||
generic)
|
||||
mkdir -p "$VAULT/wiki/sources" "$VAULT/wiki/entities" \
|
||||
"$VAULT/wiki/concepts" "$VAULT/wiki/sessions"
|
||||
say "✓ Created generic folders: sources/, entities/, concepts/, sessions/"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*) say "(skipped folder seeding)" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
say ""
|
||||
say "═══ Done. Mode is: $REQUESTED_MODE ═══"
|
||||
say ""
|
||||
say "Other skills (wiki-ingest, save, autoresearch) will consult this mode automatically."
|
||||
say "Existing files are NOT auto-migrated. New files will follow the new mode's conventions."
|
||||
say ""
|
||||
say "To switch modes later: re-run \`bash bin/setup-mode.sh\`."
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
# claude-obsidian: multi-agent skill installer
|
||||
# Symlinks the skills/ directory into each AI agent's expected location.
|
||||
# Idempotent: safe to run multiple times.
|
||||
#
|
||||
# Supported agents:
|
||||
# - Claude Code : auto-discovered via .claude-plugin/ (no symlink needed)
|
||||
# - Codex CLI : symlink to ~/.codex/skills/claude-obsidian
|
||||
# - OpenCode : symlink to ~/.opencode/skills/claude-obsidian
|
||||
# - Gemini CLI : symlink to ~/.gemini/skills/claude-obsidian
|
||||
# - Cursor : symlink to .cursor/skills (in repo)
|
||||
# - Windsurf : symlink to .windsurf/skills (in repo)
|
||||
#
|
||||
# Bootstrap files (AGENTS.md, GEMINI.md, .cursor/rules/, .windsurf/rules/,
|
||||
# .github/copilot-instructions.md) are already committed in the repo.
|
||||
# This script just wires up the skills directory.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SKILLS_DIR="$REPO_ROOT/skills"
|
||||
|
||||
if [ ! -d "$SKILLS_DIR" ]; then
|
||||
echo "ERROR: $SKILLS_DIR does not exist. Are you running this from the claude-obsidian repo?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
GRAY='\033[0;37m'
|
||||
NC='\033[0m'
|
||||
|
||||
link_if_missing() {
|
||||
local target="$1"
|
||||
local dest="$2"
|
||||
local agent_name="$3"
|
||||
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
|
||||
if [ -L "$dest" ]; then
|
||||
local existing="$(readlink "$dest")"
|
||||
if [ "$existing" = "$target" ]; then
|
||||
echo -e "${GRAY}[$agent_name] already linked: $dest${NC}"
|
||||
return
|
||||
else
|
||||
echo -e "${YELLOW}[$agent_name] symlink exists but points elsewhere: $dest -> $existing (skipping, remove manually if you want to relink)${NC}"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -e "$dest" ]; then
|
||||
echo -e "${YELLOW}[$agent_name] path exists and is not a symlink: $dest (skipping)${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
ln -s "$target" "$dest"
|
||||
echo -e "${GREEN}[$agent_name] linked: $dest -> $target${NC}"
|
||||
}
|
||||
|
||||
echo "claude-obsidian: multi-agent skill installer"
|
||||
echo "Repo: $REPO_ROOT"
|
||||
echo
|
||||
|
||||
# Codex CLI
|
||||
link_if_missing "$SKILLS_DIR" "$HOME/.codex/skills/claude-obsidian" "Codex CLI"
|
||||
|
||||
# OpenCode
|
||||
link_if_missing "$SKILLS_DIR" "$HOME/.opencode/skills/claude-obsidian" "OpenCode"
|
||||
|
||||
# Gemini CLI
|
||||
link_if_missing "$SKILLS_DIR" "$HOME/.gemini/skills/claude-obsidian" "Gemini CLI"
|
||||
|
||||
# Cursor (workspace-local)
|
||||
link_if_missing "$SKILLS_DIR" "$REPO_ROOT/.cursor/skills" "Cursor"
|
||||
|
||||
# Windsurf (workspace-local)
|
||||
link_if_missing "$SKILLS_DIR" "$REPO_ROOT/.windsurf/skills" "Windsurf"
|
||||
|
||||
echo
|
||||
echo -e "${GREEN}Done.${NC} Bootstrap files (AGENTS.md, GEMINI.md, .cursor/rules/, .windsurf/rules/, .github/copilot-instructions.md) are already in this repo."
|
||||
echo
|
||||
echo "To verify each agent picks up the skills:"
|
||||
echo " - Claude Code: open the project, type /wiki"
|
||||
echo " - Codex CLI: codex --list-skills | grep claude-obsidian"
|
||||
echo " - Cursor: open the project, ask 'what skills do you have?'"
|
||||
echo " - Windsurf: open in Cascade, ask the same"
|
||||
echo " - Gemini CLI: gemini --list-skills (if supported)"
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-retrieve.sh — opt-in bootstrap for wiki-retrieve (v1.7+).
|
||||
#
|
||||
# Provisions the contextual-prefix + BM25 + rerank pipeline. Idempotent;
|
||||
# safe to re-run after schema changes or full vault re-ingest.
|
||||
#
|
||||
# What this does (in order):
|
||||
# 1. Sanity-check that scripts/contextual-prefix.py, bm25-index.py,
|
||||
# rerank.py, retrieve.py are present and executable.
|
||||
# 2. Create .vault-meta/chunks/ and .vault-meta/bm25/ directories.
|
||||
# 3. Check for ollama + nomic-embed-text (informational; not required for
|
||||
# contextual prefix tier 2/3, but required for the rerank cosine stage).
|
||||
# 4. Data-egress consent (v1.7.1+). If a non-synthetic prefix tier
|
||||
# (anthropic-api or claude-cli) would otherwise be chosen, prompt
|
||||
# the user for explicit y/N consent. Default is abort (synthetic-only
|
||||
# remains the safe alternative). On consent, pass --allow-egress
|
||||
# through to contextual-prefix.py. Pass --no-llm at the CLI to skip
|
||||
# the prompt entirely and stay on tier-3 (synthetic).
|
||||
# 5. Run contextual-prefix.py --all to chunk + contextualize every wiki page.
|
||||
# Tier picker (synthetic by default; non-synthetic only with consent):
|
||||
# tier 1: Anthropic API (--allow-egress + ANTHROPIC_API_KEY set)
|
||||
# tier 2: claude CLI -p (--allow-egress + `claude` on PATH)
|
||||
# tier 3: synthetic (no flag, --no-llm, or no consent)
|
||||
# Stage 1 exit code is captured; non-zero aborts with a recovery hint
|
||||
# (rc=5) before Stage 2.
|
||||
# 6. Run bm25-index.py build to build the inverted index.
|
||||
#
|
||||
# After completion the wiki-retrieve skill is "feature-detected" by other
|
||||
# skills (wiki-query checks for scripts/retrieve.py + .vault-meta/chunks/).
|
||||
#
|
||||
# This is fully opt-in. Doing nothing leaves v1.6 behavior intact.
|
||||
#
|
||||
# Usage:
|
||||
# bash bin/setup-retrieve.sh
|
||||
# bash bin/setup-retrieve.sh --no-llm # force tier-3 synthetic-only
|
||||
# bash bin/setup-retrieve.sh --rebuild # rebuild all chunks
|
||||
# bash bin/setup-retrieve.sh --check # diagnostics only; no provisioning
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VAULT="$(dirname "$SCRIPT_DIR")"
|
||||
META="$VAULT/.vault-meta"
|
||||
|
||||
NO_LLM=false
|
||||
REBUILD=false
|
||||
CHECK_ONLY=false
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--no-llm) NO_LLM=true ;;
|
||||
--rebuild) REBUILD=true ;;
|
||||
--check) CHECK_ONLY=true ;;
|
||||
-h|--help)
|
||||
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "ERR: unknown flag: $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
say() { printf '%s\n' "$@"; }
|
||||
warn() { printf 'WARN: %s\n' "$@" >&2; }
|
||||
|
||||
say "═══ wiki-retrieve setup (v1.7+) ═══"
|
||||
say "Vault: $VAULT"
|
||||
say ""
|
||||
|
||||
# ── 1. Sanity check ──────────────────────────────────────────────────────────
|
||||
REQUIRED=(
|
||||
"$VAULT/scripts/contextual-prefix.py"
|
||||
"$VAULT/scripts/bm25-index.py"
|
||||
"$VAULT/scripts/rerank.py"
|
||||
"$VAULT/scripts/retrieve.py"
|
||||
)
|
||||
missing=0
|
||||
for f in "${REQUIRED[@]}"; do
|
||||
if [ ! -x "$f" ]; then
|
||||
warn "missing or not executable: $f"
|
||||
missing=$((missing+1))
|
||||
fi
|
||||
done
|
||||
if [ $missing -gt 0 ]; then
|
||||
say "FAIL: $missing required script(s) missing."
|
||||
exit 3
|
||||
fi
|
||||
say "✓ All 4 retrieval scripts present and executable"
|
||||
|
||||
# ── 2. Provision .vault-meta state directories ───────────────────────────────
|
||||
mkdir -p "$META/chunks" "$META/bm25"
|
||||
say "✓ State directories: $META/chunks/, $META/bm25/"
|
||||
|
||||
# ── 3. Check ollama (informational) ──────────────────────────────────────────
|
||||
OLLAMA_URL="${OLLAMA_URL:-http://127.0.0.1:11434}"
|
||||
# v1.9.1 / closes audit S4: if OLLAMA_URL was overridden to point off-machine,
|
||||
# refuse to probe unless the caller passes --allow-remote-ollama (mirrors the
|
||||
# existing scripts/tiling-check.py:351 gate). Same allowlist of localhost
|
||||
# patterns ollama itself recommends.
|
||||
case "$OLLAMA_URL" in
|
||||
"http://127.0.0.1:"*|"http://localhost:"*|"http://[::1]:"*) ;;
|
||||
*)
|
||||
if ! printf '%s ' "$@" | grep -q -- '--allow-remote-ollama'; then
|
||||
warn "OLLAMA_URL points off-localhost: $OLLAMA_URL"
|
||||
warn "Refusing to probe remote ollama without explicit consent."
|
||||
warn "Pass --allow-remote-ollama to bin/setup-retrieve.sh to opt in, or"
|
||||
warn "unset OLLAMA_URL to use the default http://127.0.0.1:11434."
|
||||
OLLAMA_URL=""
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
OLLAMA_ALIVE=false
|
||||
MODEL_PRESENT=false
|
||||
if [ -n "$OLLAMA_URL" ] && command -v curl >/dev/null 2>&1; then
|
||||
if curl -fsS --max-time 3 "$OLLAMA_URL/api/tags" >/dev/null 2>&1; then
|
||||
OLLAMA_ALIVE=true
|
||||
if curl -fsS --max-time 3 "$OLLAMA_URL/api/tags" 2>/dev/null \
|
||||
| grep -q '"nomic-embed-text'; then
|
||||
MODEL_PRESENT=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if $OLLAMA_ALIVE && $MODEL_PRESENT; then
|
||||
say "✓ ollama reachable at $OLLAMA_URL with nomic-embed-text pulled (rerank will use cosine)"
|
||||
elif $OLLAMA_ALIVE; then
|
||||
warn "ollama reachable but nomic-embed-text is not pulled. Run: ollama pull nomic-embed-text"
|
||||
warn "rerank stage will no-op until the model is available."
|
||||
else
|
||||
warn "ollama not reachable at $OLLAMA_URL"
|
||||
warn "rerank stage will no-op until ollama is running. BM25 retrieval still works."
|
||||
warn "Install: https://ollama.com/download; then: ollama pull nomic-embed-text"
|
||||
fi
|
||||
|
||||
# ── 4. Prefix-tier picker (informational) ────────────────────────────────────
|
||||
# v1.7.1: tier reflects what WOULD run if --allow-egress were passed.
|
||||
# Without consent, the actual run forces tier-3 synthetic.
|
||||
if $NO_LLM; then
|
||||
PREFIX_TIER="synthetic (forced via --no-llm)"
|
||||
elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
||||
PREFIX_TIER="anthropic-api (ANTHROPIC_API_KEY detected; ~\$12/1000 docs)"
|
||||
elif command -v claude >/dev/null 2>&1; then
|
||||
PREFIX_TIER="claude-cli subprocess (no API key needed; uses CC subscription)"
|
||||
else
|
||||
PREFIX_TIER="synthetic (no API key, no claude CLI; reduced retrieval quality)"
|
||||
fi
|
||||
say "✓ Contextual-prefix tier (if --allow-egress): $PREFIX_TIER"
|
||||
|
||||
if $CHECK_ONLY; then
|
||||
say ""
|
||||
say "── --check passed; not provisioning."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── 4b. Egress consent (v1.7.1) ──────────────────────────────────────────────
|
||||
# If a non-synthetic tier would otherwise be selected, require explicit consent
|
||||
# before letting contextual-prefix.py send page bodies off-machine. Mirrors the
|
||||
# --allow-remote-ollama precedent in scripts/tiling-check.py.
|
||||
ALLOW_EGRESS=false
|
||||
if ! $NO_LLM; then
|
||||
case "$PREFIX_TIER" in
|
||||
anthropic-api*|claude-cli*)
|
||||
say ""
|
||||
say "⚠️ Stage 1 will send wiki page BODIES off-machine via the '$PREFIX_TIER' tier."
|
||||
say " Estimated cost: ~\$0 (claude-cli, free) to ~\$12 per 1,000 pages (Anthropic API)."
|
||||
say " Per-page bodies are POSTed to the provider; review their privacy policy first."
|
||||
say " Default is NO. Tier-3 (synthetic, on-machine) is the safe alternative."
|
||||
printf " Continue with egress? [y/N]: "
|
||||
read -r reply || reply=""
|
||||
case "$reply" in
|
||||
[yY]|[yY][eE][sS])
|
||||
say "→ Proceeding with egress."
|
||||
ALLOW_EGRESS=true
|
||||
;;
|
||||
*)
|
||||
say "→ Aborted. Re-run with --no-llm for the synthetic-only path,"
|
||||
say " or set ANTHROPIC_API_KEY and use the claude CLI deliberately."
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── 5. Chunk + contextualize every wiki page ─────────────────────────────────
|
||||
say ""
|
||||
say "═══ Stage 1/2: chunking + contextual-prefix generation ═══"
|
||||
ARGS=("--all")
|
||||
$NO_LLM && ARGS+=("--no-llm")
|
||||
$ALLOW_EGRESS && ARGS+=("--allow-egress")
|
||||
$REBUILD && ARGS+=("--rebuild")
|
||||
# Disable set -e for the call so we can inspect the exit code and offer a
|
||||
# concrete recovery hint instead of aborting with a bare trace.
|
||||
set +e
|
||||
python3 "$VAULT/scripts/contextual-prefix.py" "${ARGS[@]}"
|
||||
STAGE1_RC=$?
|
||||
set -e
|
||||
if [ "$STAGE1_RC" -ne 0 ]; then
|
||||
warn "Stage 1 failed (rc=$STAGE1_RC). Partial chunks may exist at:"
|
||||
warn " $META/chunks/"
|
||||
warn "Recovery options:"
|
||||
warn " 1. Re-run setup-retrieve.sh — body_hash skips already-processed chunks."
|
||||
warn " 2. Wipe and start over: rm -rf $META/chunks/ && bash bin/setup-retrieve.sh"
|
||||
warn " 3. Re-process one page: python3 scripts/contextual-prefix.py wiki/<failing-page>.md --rebuild"
|
||||
exit 5
|
||||
fi
|
||||
|
||||
# ── 6. Build BM25 index ──────────────────────────────────────────────────────
|
||||
say ""
|
||||
say "═══ Stage 2/2: BM25 index build ═══"
|
||||
python3 "$VAULT/scripts/bm25-index.py" build
|
||||
|
||||
# ── 7. Smoke-test retrieve.py ────────────────────────────────────────────────
|
||||
say ""
|
||||
say "═══ Smoke test ═══"
|
||||
SMOKE_OUT="$(python3 "$VAULT/scripts/retrieve.py" "wiki" --top 1 2>/dev/null || echo '{}')"
|
||||
if echo "$SMOKE_OUT" | grep -q '"candidates":'; then
|
||||
say "✓ retrieve.py returns valid JSON"
|
||||
else
|
||||
warn "retrieve.py smoke test produced unexpected output. Run manually for details."
|
||||
fi
|
||||
|
||||
say ""
|
||||
say "═══ wiki-retrieve is provisioned. ═══"
|
||||
say ""
|
||||
say "Usage from the command line:"
|
||||
say " python3 scripts/retrieve.py \"your question here\" --top 5"
|
||||
say ""
|
||||
say "Other skills (wiki-query, autoresearch) will now automatically use the"
|
||||
say "hybrid pipeline when answering questions. See skills/wiki-retrieve/SKILL.md."
|
||||
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env bash
|
||||
# claude-obsidian vault setup script
|
||||
# Run this ONCE before opening Obsidian for the first time.
|
||||
# Usage: bash bin/setup-vault.sh [optional: /path/to/vault]
|
||||
# Default: uses the directory where this script lives (the vault root)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VAULT="${1:-$(dirname "$SCRIPT_DIR")}"
|
||||
OBSIDIAN="$VAULT/.obsidian"
|
||||
|
||||
echo "Setting up claude-obsidian vault at: $VAULT"
|
||||
|
||||
# ── 1. Create directories ─────────────────────────────────────────────────────
|
||||
mkdir -p "$OBSIDIAN/snippets"
|
||||
mkdir -p "$VAULT/.raw"
|
||||
mkdir -p "$VAULT/wiki/concepts" "$VAULT/wiki/entities" "$VAULT/wiki/sources" "$VAULT/wiki/meta"
|
||||
mkdir -p "$VAULT/_templates"
|
||||
|
||||
# ── 2. Write graph.json ───────────────────────────────────────────────────────
|
||||
cat > "$OBSIDIAN/graph.json" << 'EOF'
|
||||
{
|
||||
"collapse-filter": false,
|
||||
"search": "path:wiki",
|
||||
"showTags": false,
|
||||
"showAttachments": false,
|
||||
"hideUnresolved": true,
|
||||
"showOrphans": false,
|
||||
"collapse-color-groups": false,
|
||||
"colorGroups": [
|
||||
{ "query": "path:wiki/entities", "color": { "a": 1, "rgb": 12945088 } },
|
||||
{ "query": "path:wiki/concepts", "color": { "a": 1, "rgb": 5227007 } },
|
||||
{ "query": "path:wiki/sources", "color": { "a": 1, "rgb": 6986069 } },
|
||||
{ "query": "path:wiki/meta", "color": { "a": 1, "rgb": 5676246 } },
|
||||
{ "query": "path:wiki", "color": { "a": 1, "rgb": 5676246 } }
|
||||
],
|
||||
"showArrow": true,
|
||||
"textFadeMultiplier": -1,
|
||||
"nodeSizeMultiplier": 1.8,
|
||||
"lineSizeMultiplier": 1.2,
|
||||
"centerStrength": 0.5,
|
||||
"repelStrength": 30,
|
||||
"linkStrength": 1.5,
|
||||
"linkDistance": 120,
|
||||
"scale": 1.0
|
||||
}
|
||||
EOF
|
||||
|
||||
# ── 3. Write app.json (excluded files) ───────────────────────────────────────
|
||||
cat > "$OBSIDIAN/app.json" << 'EOF'
|
||||
{
|
||||
"userIgnoreFilters": [
|
||||
"agents/",
|
||||
"commands/",
|
||||
"hooks/",
|
||||
"skills/",
|
||||
"_templates/",
|
||||
"README.md",
|
||||
"CLAUDE.md",
|
||||
"WIKI.md",
|
||||
"Welcome.md"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# ── 4. Write appearance.json (enable CSS snippets) ───────────────────────────
|
||||
cat > "$OBSIDIAN/appearance.json" << 'EOF'
|
||||
{
|
||||
"enabledCssSnippets": [
|
||||
"vault-colors",
|
||||
"ITS-Dataview-Cards",
|
||||
"ITS-Image-Adjustments"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# ── 5. Download Excalidraw main.js (8MB, not in git) ─────────────────────────
|
||||
EXCALIDRAW="$OBSIDIAN/plugins/obsidian-excalidraw-plugin"
|
||||
if [ -f "$EXCALIDRAW/manifest.json" ] && [ ! -f "$EXCALIDRAW/main.js" ]; then
|
||||
echo "Downloading Excalidraw main.js (~8MB)..."
|
||||
curl -sS -L \
|
||||
"https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/latest/download/main.js" \
|
||||
-o "$EXCALIDRAW/main.js"
|
||||
echo "✓ Excalidraw main.js downloaded"
|
||||
elif [ -f "$EXCALIDRAW/main.js" ]; then
|
||||
echo "✓ Excalidraw main.js already present"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✓ Setup complete."
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Open Obsidian"
|
||||
echo " 2. Manage Vaults → Open folder as vault → select: $VAULT"
|
||||
echo " 3. Enable community plugins when prompted (Calendar, Thino, Excalidraw, Banners are pre-installed)"
|
||||
echo " 4. Install: Dataview, Templater, Obsidian Git (Settings → Community Plugins)"
|
||||
echo " 5. Type /wiki in Claude Code to scaffold your knowledge base"
|
||||
echo ""
|
||||
echo "Pre-installed plugins:"
|
||||
echo " - Calendar (sidebar calendar with word count + task dots)"
|
||||
echo " - Thino (quick memo capture)"
|
||||
echo " - Excalidraw (freehand drawing + image annotation)"
|
||||
echo " - Banners (add banner: to any note frontmatter for header images)"
|
||||
echo ""
|
||||
echo "CSS snippets enabled:"
|
||||
echo " - vault-colors: color-codes wiki/ folders in file explorer"
|
||||
echo " - ITS-Dataview-Cards: use \`\`\`dataviewjs with .cards for card grids"
|
||||
echo " - ITS-Image-Adjustments: append |100 to image embeds for sizing"
|
||||
echo ""
|
||||
echo "Views available:"
|
||||
echo " - Wiki Map canvas (wiki/Wiki Map.canvas) — knowledge graph"
|
||||
echo " - Design Ideas canvas (projects/visual-vault/design-ideas.canvas) — visual reference board"
|
||||
echo " - Graph view filtered to wiki/ only, color-coded by type"
|
||||
echo ""
|
||||
echo "To switch to the visual layout (Canvas + Calendar + Thino sidebar):"
|
||||
echo " Quit Obsidian, then run:"
|
||||
echo " cp $OBSIDIAN/workspace-visual.json $OBSIDIAN/workspace.json"
|
||||
echo " Then reopen Obsidian."
|
||||
echo ""
|
||||
echo "Graph colors: if they reset after closing Obsidian, open Graph settings"
|
||||
echo "→ Color groups and re-add them once. They persist permanently after that."
|
||||
Reference in New Issue
Block a user