obsidian-knowledge 3.26.2__tar.gz → 3.26.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude-plugin/marketplace.json +1 -1
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude-plugin/plugin.json +1 -1
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.codex-plugin/plugin.json +1 -1
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/PKG-INFO +1 -1
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/.codex-plugin/plugin.json +1 -1
- obsidian_knowledge-3.26.3/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +98 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/convention-sweep.py +14 -32
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/edit-index.py +9 -4
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/filter-unresolved-links.py +14 -15
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/find-open-questions.py +20 -24
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/broken-links.md +4 -15
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/index-format.md +7 -12
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/rename-files.md +2 -4
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
- obsidian_knowledge-3.26.3/plugins/obsidian-knowledge/skills/vault-organizer/organizer_context.py +30 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/recover-unresolved-links.py +12 -16
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/vault-audit.py +13 -18
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/pyproject.toml +1 -1
- obsidian_knowledge-3.26.3/skills/vault-organizer/SKILL.md +98 -0
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/convention-sweep.py +14 -32
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/edit-index.py +9 -4
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/filter-unresolved-links.py +14 -15
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/find-open-questions.py +20 -24
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/broken-links.md +4 -15
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/index-format.md +7 -12
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/rename-files.md +2 -4
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
- obsidian_knowledge-3.26.3/skills/vault-organizer/organizer_context.py +30 -0
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/recover-unresolved-links.py +12 -16
- {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/vault-audit.py +13 -18
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_gardener_helpers.py +78 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/uv.lock +1 -1
- obsidian_knowledge-3.26.2/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +0 -166
- obsidian_knowledge-3.26.2/skills/vault-organizer/SKILL.md +0 -166
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.agents/plugins/marketplace.json +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/agents.red-green-tdd.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.doc-code-coupling.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.no-junk-drawers.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.parse-dont-validate.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.require-plugin-version-bump.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.semantic-types.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.taste-enforcer.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/ci.yml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/docs.yml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/publish.yml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.gitignore +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.secrets.baseline +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/AGENTS.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/CONVENTIONS.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/LICENSE +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/README.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/commands/scan-secrets.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/.pages +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/ARCHITECTURE.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/CLI.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/MCP.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/QUALITY.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/TODO.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/configuration.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/contributing-docs.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/hooks.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/index.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/plans/2026-04-25-improve-harness-implementation.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/plans/2026-05-12-changelog-reform.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/specs/2026-04-25-improve-harness-design.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/specs/2026-05-12-changelog-reform-design.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/capture-session.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/codex-hooks.json +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/doctor.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/__init__.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/capture.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/patterns.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/recall_init_lib.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/reflect_counter.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/repo_memory.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/secret_filters.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/shell_guard.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/stop_hook.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/transcript.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/vault_config.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/vault_policy.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/i-insist.toml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/i_insist.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/nudge-index-sync.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/recall-init.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/reflect-nudge.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/remind-convos.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/remind-convos.sh +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/scan-vault-secrets.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/update-changelog.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/update-changelog.sh +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/vault_registry.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/__init__.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/__init__.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/cli.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/config.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/filters.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/guard_install.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/indexer.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/mcp_server.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/models.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/papercuts.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/primer.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/registration.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/vault_files.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/mkdocs.yml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/commands/scan-secrets.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/capture-session.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/doctor.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hooks.json +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/__init__.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/capture.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/patterns.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/recall_init_lib.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/reflect_counter.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/repo_memory.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/secret_filters.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/shell_guard.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/stop_hook.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/transcript.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/vault_config.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/vault_policy.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/i-insist.toml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/i_insist.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/nudge-index-sync.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/recall-init.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/reflect-nudge.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/remind-convos.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/remind-convos.sh +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/scan-vault-secrets.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/update-changelog.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/update-changelog.sh +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/vault_registry.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/obsidian-knowledge/SKILL.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/remember-conversations/SKILL.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/note-types.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/state-files.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/link_recovery_models.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/organizer_io.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/prek.toml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/build_memory_indexes.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/cli_smoke_test.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/migrate_changelog.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/migrate_claude_memory.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/new-worktree.sh +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_architecture.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_exception_handling.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_file_length.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_private_test_imports.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_timeless_comments.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prepare_release.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/smoke_tool_install.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/sync_codex_plugin.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/obsidian-knowledge/SKILL.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/remember-conversations/SKILL.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/note-types.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/state-files.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/link_recovery_models.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/organizer_io.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/__init__.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/conftest.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/.claude/obsidian-knowledge.yaml +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/Inbox/random.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/Journal/diary.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/wiki/python.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/wiki/rust.md +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/transcript_new_file_no_index.jsonl +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/transcript_new_file_with_index.jsonl +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_access_errors.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_architecture_check.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_capture_instructions.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_capture_session_hook.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_dispatch.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_reliability.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_smoke.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_config.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_doctor.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_filters.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_first_install.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_i_insist_rules.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_indexer.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_indexer_reliability.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_mcp.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_mcp_download.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_migrate_changelog.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_nudge_index_sync.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_papercut_cli.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_papercuts.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_patterns.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_primer.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_protect_vault_destructive_ops.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_public_api.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recall_init.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recall_init_lib.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recover_unresolved_links.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_reflect_counter.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_reflect_nudge.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_release.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_repo_memory.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_scan_vault_secrets.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_search_json.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_secret_scan_filters.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_session_debounce.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_transcript.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_boundaries.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_files.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_policy.py +0 -0
- {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "obsidian-knowledge",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.3+codex.20261003154117",
|
|
4
4
|
"description": "Plugin that manages an Obsidian knowledge vault: capture, organize, index, and garden.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ricardo Decal"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vault-organizer
|
|
3
|
+
description: >-
|
|
4
|
+
This skill should be used when the user asks to "organize the vault",
|
|
5
|
+
"update indexes", "fix broken links", "rename ambiguous files", "fix
|
|
6
|
+
filenames", "garden the vault", "sync indexes", "clean up the vault",
|
|
7
|
+
"maintain the vault", or after making substantial structural edits
|
|
8
|
+
(creating, moving, renaming, or deleting files) in an Obsidian vault.
|
|
9
|
+
Also triggered by scheduled cron invocations for routine vault maintenance.
|
|
10
|
+
version: 1.4.12
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Vault organizer
|
|
14
|
+
|
|
15
|
+
Maintain indexes, links, filenames, and note locations. Preserve primary note
|
|
16
|
+
content except for intended link and frontmatter repairs. Read each `lib/`
|
|
17
|
+
reference only when its step applies.
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Set `SCRIPTS` to the directory containing the loaded `SKILL.md`. Use that copy,
|
|
22
|
+
not an arbitrary plugin cache or the working directory. Scripts declare their
|
|
23
|
+
own dependencies; run them with `uv run`.
|
|
24
|
+
|
|
25
|
+
Scripts default to the configured vault containing cwd, or the sole registered
|
|
26
|
+
vault. Pass a vault root as the first argument to override it. When multiple
|
|
27
|
+
vaults are configured and cwd selects none, an explicit root is required.
|
|
28
|
+
|
|
29
|
+
Read the vault's local instructions, `.claude/obsidian-knowledge.yaml`, and
|
|
30
|
+
`Utility/obsidian-knowledge/needs-attention.md`. Set `VAULT_NAME` to its registered
|
|
31
|
+
Obsidian name. Keep `vault="$VAULT_NAME"` before every Obsidian subcommand; after
|
|
32
|
+
it, the CLI can silently ignore the option. Enable automatic internal-link
|
|
33
|
+
updates in Obsidian.
|
|
34
|
+
|
|
35
|
+
Exclude hidden files, protected sources, and Syncthing sync conflicts from
|
|
36
|
+
maintenance. Handle conflicts separately. Use `obsidian-knowledge write` for
|
|
37
|
+
manual Markdown edits; its writer verifies the result. Helpers already use it.
|
|
38
|
+
|
|
39
|
+
## Structure
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv run "$SCRIPTS/vault-audit.py"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Triage all findings:
|
|
46
|
+
|
|
47
|
+
- `MISSING_INDEX`, `NOT_INDEXED`: create or complete indexes using
|
|
48
|
+
[index conventions](lib/index-format.md). Read children to write useful
|
|
49
|
+
orientation phrases; a descendant link does not replace a child-index link.
|
|
50
|
+
- `EMPTY_FOLDER`: check sync state and local layout. Do not create empty indexes
|
|
51
|
+
or delete folders automatically.
|
|
52
|
+
- `DUMPING_GROUND`: classify misplaced dated, design, diary, and conversation
|
|
53
|
+
notes by meaning. Follow [note locations](lib/note-types.md).
|
|
54
|
+
- `STACKED_FRONTMATTER`: use `uv run "$SCRIPTS/fix-stacked-frontmatter.py" --fix NOTE_PATH`.
|
|
55
|
+
`NEEDS_MERGE` requires [manual frontmatter repair](lib/stacked-frontmatter.md).
|
|
56
|
+
|
|
57
|
+
Rename ambiguous files using [rename guidance](lib/rename-files.md).
|
|
58
|
+
Use Obsidian move and rename commands so internal links update.
|
|
59
|
+
|
|
60
|
+
## Links
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
obsidian vault="$VAULT_NAME" unresolved verbose format=json | uv run "$SCRIPTS/recover-unresolved-links.py"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Inspect the report. Add `--apply` for unique recoveries; triage remaining links
|
|
67
|
+
using [broken-link guidance](lib/broken-links.md). Preserve aliases, headings,
|
|
68
|
+
block references, and primary text. Leave intentional concept stubs intact.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
obsidian vault="$VAULT_NAME" orphans
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Add managed notes to their parent indexes where appropriate. Respect established
|
|
75
|
+
exceptions and ignore orphans outside managed zones.
|
|
76
|
+
|
|
77
|
+
## Conventions and reports
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv run "$SCRIPTS/convention-sweep.py"
|
|
81
|
+
uv run "$SCRIPTS/find-open-questions.py" --apply
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Fix `WIKILINK_EXT` (`.md` in note links), `UNDATED_FILE` (missing required date
|
|
85
|
+
prefix), and `YAML_ERR`. The question report defaults to
|
|
86
|
+
`Utility/obsidian-knowledge/reports/open-questions.md`, preserving its header,
|
|
87
|
+
frontmatter, and scope. Use `--report` to preview or `--timestamp` to override
|
|
88
|
+
current local time. With no flags, the scanner emits TSV.
|
|
89
|
+
|
|
90
|
+
## Finish
|
|
91
|
+
|
|
92
|
+
Re-run affected checks. Review changed links and fresh CLI results; reload
|
|
93
|
+
Obsidian only if its cache disagrees with file contents. No checksum comparison
|
|
94
|
+
is needed for an ordinary CLI move or rename.
|
|
95
|
+
|
|
96
|
+
Update unresolved items in `needs-attention.md` and record completed vault
|
|
97
|
+
changes in one same-session changelog fragment. Follow [state-file conventions](lib/state-files.md);
|
|
98
|
+
do not edit a shared changelog index. Skip logging when no vault changes occurred.
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["pyyaml"]
|
|
5
|
+
# ///
|
|
2
6
|
"""convention-sweep: vault-wide check for write-time convention violations.
|
|
3
7
|
|
|
4
|
-
Usage:
|
|
8
|
+
Usage: uv run convention-sweep.py [vault_root]
|
|
5
9
|
|
|
6
10
|
Walks all `.md` files under <vault_root> (skipping dotfolders, _sources/,
|
|
7
11
|
.trash/, node_modules/) and runs the same three checks as the
|
|
@@ -22,31 +26,14 @@ needs-attention.md entry format.
|
|
|
22
26
|
|
|
23
27
|
from __future__ import annotations
|
|
24
28
|
|
|
25
|
-
import
|
|
29
|
+
import argparse
|
|
26
30
|
from pathlib import Path
|
|
27
31
|
|
|
32
|
+
from organizer_context import resolve_vault
|
|
28
33
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
# Source checkout layout is:
|
|
33
|
-
# <repo>/plugins/obsidian-knowledge/hooks/hookslib/patterns.py
|
|
34
|
-
# <repo>/skills/vault-organizer/convention-sweep.py
|
|
35
|
-
def _find_hooks_dir() -> Path:
|
|
36
|
-
here = Path(__file__).resolve()
|
|
37
|
-
candidates = [
|
|
38
|
-
here.parent.parent.parent / "hooks",
|
|
39
|
-
here.parent.parent.parent / "plugins" / "obsidian-knowledge" / "hooks",
|
|
40
|
-
]
|
|
41
|
-
for candidate in candidates:
|
|
42
|
-
if (candidate / "hookslib" / "patterns.py").exists():
|
|
43
|
-
return candidate
|
|
44
|
-
raise ModuleNotFoundError("Could not locate hooks/hookslib/patterns.py from convention-sweep.py")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sys.path.insert(0, str(_find_hooks_dir()))
|
|
48
|
-
|
|
49
|
-
from hookslib.patterns import ( # noqa: E402
|
|
34
|
+
# isort: split
|
|
35
|
+
# organizer_context puts the plugin's hooks on sys.path before this import.
|
|
36
|
+
from hookslib.patterns import (
|
|
50
37
|
DATE_PREFIX_RE,
|
|
51
38
|
PERIODIC_NOTE_RE,
|
|
52
39
|
find_wikilink_ext_violations,
|
|
@@ -111,14 +98,9 @@ def print_header(lib_dir: Path, counts: dict[str, int]) -> None:
|
|
|
111
98
|
|
|
112
99
|
|
|
113
100
|
def main() -> None:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
vault_root = Path(sys.argv[1]).resolve()
|
|
119
|
-
if not vault_root.is_dir():
|
|
120
|
-
print(f"Error: {vault_root} is not a directory", file=sys.stderr)
|
|
121
|
-
sys.exit(1)
|
|
101
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
102
|
+
parser.add_argument("vault_root", type=Path, nargs="?", help="default: configured vault")
|
|
103
|
+
vault_root = resolve_vault(parser.parse_args().vault_root)
|
|
122
104
|
|
|
123
105
|
lib_dir = Path(__file__).parent / "lib"
|
|
124
106
|
issues = sweep(vault_root)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["pyyaml", "pydantic>=2"]
|
|
5
|
+
# ///
|
|
2
6
|
"""Render reviewed JSON entries into one index section; --apply verifies writes.
|
|
3
7
|
|
|
4
|
-
Usage: edit-index.py VAULT wiki/topic/index.md [--apply] < reviewed.json
|
|
8
|
+
Usage: uv run edit-index.py [VAULT] wiki/topic/index.md [--apply] < reviewed.json
|
|
5
9
|
Input: {"section": "Notes", "entries": [{"target": "wiki/topic/note",
|
|
6
10
|
"description": "orientation", "label": "optional display name"}]}
|
|
7
11
|
For a new flat index, supply "title" instead of "section". Existing prose and
|
|
@@ -15,6 +19,7 @@ import re
|
|
|
15
19
|
import sys
|
|
16
20
|
from pathlib import Path
|
|
17
21
|
|
|
22
|
+
from organizer_context import resolve_vault
|
|
18
23
|
from organizer_io import OrganizerPolicy, visible_path, write_checked
|
|
19
24
|
from pydantic import BaseModel, ConfigDict, field_validator
|
|
20
25
|
|
|
@@ -156,12 +161,12 @@ def render_index(vault: Path, index: Path, before: str | None, review: IndexRevi
|
|
|
156
161
|
|
|
157
162
|
def main() -> None:
|
|
158
163
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
159
|
-
parser.add_argument("vault_root", type=Path)
|
|
164
|
+
parser.add_argument("vault_root", type=Path, nargs="?", help="default: configured vault")
|
|
160
165
|
parser.add_argument("index_path")
|
|
161
166
|
parser.add_argument("--apply", action="store_true")
|
|
162
167
|
args = parser.parse_args()
|
|
163
168
|
try:
|
|
164
|
-
vault = args.vault_root
|
|
169
|
+
vault = resolve_vault(args.vault_root)
|
|
165
170
|
if Path(args.index_path).name != "index.md":
|
|
166
171
|
raise ValueError("edit-index only edits index.md")
|
|
167
172
|
index = OrganizerPolicy.load(vault).writable(vault, args.index_path)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["pyyaml"]
|
|
5
|
+
# ///
|
|
2
6
|
"""filter-unresolved-links: filter `obsidian unresolved` JSON output.
|
|
3
7
|
|
|
4
8
|
Usage:
|
|
5
|
-
obsidian unresolved verbose format=json |
|
|
9
|
+
obsidian unresolved verbose format=json | uv run filter-unresolved-links.py [vault_root]
|
|
6
10
|
|
|
7
11
|
Reads `stub_link_patterns:` from <vault_root>/.claude/obsidian-knowledge.yaml
|
|
8
12
|
to know which link names are intentional concept-stubs (not real broken
|
|
@@ -20,22 +24,19 @@ Emits the surviving entries one per line:
|
|
|
20
24
|
|
|
21
25
|
from __future__ import annotations
|
|
22
26
|
|
|
27
|
+
import argparse
|
|
23
28
|
import json
|
|
24
29
|
import re
|
|
25
30
|
import signal
|
|
26
31
|
import sys
|
|
27
32
|
from pathlib import Path
|
|
28
33
|
|
|
34
|
+
import yaml
|
|
35
|
+
from organizer_context import resolve_vault
|
|
36
|
+
|
|
29
37
|
# Don't crash with a BrokenPipeError when stdout is piped to head/less.
|
|
30
38
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
31
39
|
|
|
32
|
-
try:
|
|
33
|
-
import yaml
|
|
34
|
-
|
|
35
|
-
HAS_YAML = True
|
|
36
|
-
except ImportError:
|
|
37
|
-
HAS_YAML = False
|
|
38
|
-
|
|
39
40
|
DEFAULT_STUB_PATTERNS = [
|
|
40
41
|
r"^\(PAPER\) ",
|
|
41
42
|
r"^\(VIDEO\) ",
|
|
@@ -56,7 +57,7 @@ def load_config(vault_root: Path) -> tuple[list[str], list[re.Pattern]]:
|
|
|
56
57
|
patterns_raw = DEFAULT_STUB_PATTERNS
|
|
57
58
|
|
|
58
59
|
config_path = vault_root / ".claude" / "obsidian-knowledge.yaml"
|
|
59
|
-
if
|
|
60
|
+
if config_path.exists():
|
|
60
61
|
with open(config_path) as f:
|
|
61
62
|
cfg = yaml.safe_load(f) or {}
|
|
62
63
|
zones = cfg.get("ai_managed", zones)
|
|
@@ -73,11 +74,9 @@ def is_stub(link: str, patterns: list[re.Pattern]) -> bool:
|
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def main() -> None:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
vault_root = Path(sys.argv[1]).resolve()
|
|
77
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
78
|
+
parser.add_argument("vault_root", type=Path, nargs="?", help="default: configured vault")
|
|
79
|
+
vault_root = resolve_vault(parser.parse_args().vault_root)
|
|
81
80
|
zones, patterns = load_config(vault_root)
|
|
82
81
|
zone_prefixes = tuple(f"{z}/" for z in zones)
|
|
83
82
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["pyyaml", "pydantic>=2"]
|
|
5
|
+
# ///
|
|
2
6
|
"""find-open-questions: scan ai_managed zones for `> [!question]` callouts.
|
|
3
7
|
|
|
4
|
-
Usage:
|
|
8
|
+
Usage: uv run find-open-questions.py [vault_root]
|
|
5
9
|
|
|
6
10
|
Skips matches inside fenced code blocks (``` or ~~~), which are usually
|
|
7
11
|
documentation examples rather than real open questions. Emits one line per
|
|
@@ -9,8 +13,8 @@ hit:
|
|
|
9
13
|
|
|
10
14
|
<relative_path>\t<line_number>\t<question_text>
|
|
11
15
|
|
|
12
|
-
Add --report
|
|
13
|
-
|
|
16
|
+
Add --report to render the standard Markdown report, --timestamp for
|
|
17
|
+
reproducible output, or --apply to regenerate the report in place.
|
|
14
18
|
Reads ai_managed zones from
|
|
15
19
|
<vault_root>/.claude/obsidian-knowledge.yaml; falls back to ['wiki'].
|
|
16
20
|
"""
|
|
@@ -23,18 +27,15 @@ import sys
|
|
|
23
27
|
from datetime import datetime
|
|
24
28
|
from pathlib import Path
|
|
25
29
|
|
|
30
|
+
import yaml
|
|
31
|
+
from organizer_context import resolve_vault
|
|
26
32
|
from organizer_io import visible_path, write_checked
|
|
27
33
|
from pydantic import BaseModel, ConfigDict
|
|
28
34
|
|
|
29
|
-
try:
|
|
30
|
-
import yaml
|
|
31
|
-
|
|
32
|
-
HAS_YAML = True
|
|
33
|
-
except ImportError:
|
|
34
|
-
HAS_YAML = False
|
|
35
|
-
|
|
36
35
|
QUESTION_MARKER = "> [!question]"
|
|
37
36
|
SCAN_SKIP_DIR_NAMES = {"_sources", ".trash", "node_modules"}
|
|
37
|
+
# NOTE: Standard report location is documented in SKILL.md, Conventions and reports.
|
|
38
|
+
REPORT_PATH = "Utility/obsidian-knowledge/reports/open-questions.md"
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class QuestionHit(BaseModel):
|
|
@@ -79,7 +80,7 @@ def render_report(before: str | None, hits: tuple[QuestionHit, ...], timestamp:
|
|
|
79
80
|
|
|
80
81
|
def load_managed_zones(vault_root: Path) -> list[str]:
|
|
81
82
|
config_path = vault_root / ".claude" / "obsidian-knowledge.yaml"
|
|
82
|
-
if
|
|
83
|
+
if config_path.exists():
|
|
83
84
|
with open(config_path) as f:
|
|
84
85
|
cfg = yaml.safe_load(f) or {}
|
|
85
86
|
return cfg.get("ai_managed", ["wiki"])
|
|
@@ -130,19 +131,14 @@ def scan_file(path: Path) -> list[tuple[int, str]]:
|
|
|
130
131
|
|
|
131
132
|
def main() -> None:
|
|
132
133
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
133
|
-
parser.add_argument("vault_root", type=Path)
|
|
134
|
-
parser.add_argument("--report", help="render the
|
|
134
|
+
parser.add_argument("vault_root", type=Path, nargs="?", help="default: configured vault")
|
|
135
|
+
parser.add_argument("--report", nargs="?", const=REPORT_PATH, help="render the standard report (dry run)")
|
|
135
136
|
parser.add_argument("--timestamp", help="timezone-aware ISO timestamp; default: current local time")
|
|
136
|
-
parser.add_argument(
|
|
137
|
-
"--apply", action="store_true", help="write the reviewed report using the verified writer"
|
|
138
|
-
)
|
|
137
|
+
parser.add_argument("--apply", action="store_true", help="regenerate the standard report")
|
|
139
138
|
args = parser.parse_args()
|
|
140
|
-
if args.apply
|
|
141
|
-
|
|
142
|
-
vault_root = args.vault_root
|
|
143
|
-
if not vault_root.is_dir():
|
|
144
|
-
print(f"Error: {vault_root} is not a directory", file=sys.stderr)
|
|
145
|
-
sys.exit(1)
|
|
139
|
+
if args.apply:
|
|
140
|
+
args.report = args.report or REPORT_PATH
|
|
141
|
+
vault_root = resolve_vault(args.vault_root)
|
|
146
142
|
|
|
147
143
|
zones = load_managed_zones(vault_root)
|
|
148
144
|
hits = []
|
|
@@ -166,7 +162,7 @@ def main() -> None:
|
|
|
166
162
|
print(f"{hit.path}\t{hit.line}\t{hit.text}")
|
|
167
163
|
return
|
|
168
164
|
try:
|
|
169
|
-
if args.report !=
|
|
165
|
+
if args.report != REPORT_PATH:
|
|
170
166
|
raise ValueError("--report must target Utility/obsidian-knowledge/reports/open-questions.md")
|
|
171
167
|
stamp = datetime.fromisoformat(args.timestamp) if args.timestamp else datetime.now().astimezone()
|
|
172
168
|
if stamp.utcoffset() is None:
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
2
6
|
"""fix-stacked-frontmatter: detect and auto-fix stray duplicate `---` markers.
|
|
3
7
|
|
|
4
8
|
The most common cause of STACKED_FRONTMATTER is a stray duplicate `---` line
|
|
@@ -10,8 +14,8 @@ are left alone and reported for manual review, since merging may need
|
|
|
10
14
|
human judgment about which keys win.
|
|
11
15
|
|
|
12
16
|
Usage:
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
uv run fix-stacked-frontmatter.py <file> [<file>...] # dry run
|
|
18
|
+
uv run fix-stacked-frontmatter.py --fix <file> [<file>...] # rewrite
|
|
15
19
|
|
|
16
20
|
Exit 0 if no issues or all fixed; exit 1 if files need manual merge.
|
|
17
21
|
"""
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Unresolved links
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
obsidian vault="$VAULT_NAME" unresolved verbose format=json | uv run --no-project --with pyyaml python "$SCRIPTS/filter-unresolved-links.py" "$VAULT"
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
The filter keeps links from `ai_managed` zones and excludes template placeholders
|
|
5
|
+
The recovery script keeps links from `ai_managed` zones and excludes template placeholders
|
|
12
6
|
and configured `stub_link_patterns` from `.claude/obsidian-knowledge.yaml`.
|
|
13
7
|
Defaults cover prefixes such as `(PAPER)`, `(BOOK)`, and `@Person`.
|
|
14
8
|
|
|
@@ -40,14 +34,9 @@ from candidates. Sources must be visible managed Markdown notes outside
|
|
|
40
34
|
`_sources/` and configured read-only paths. Apply rechecks write policy and
|
|
41
35
|
delegates to the verified writer; aliases, heading and block suffixes survive.
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
fresh `obsidian vault="$VAULT_NAME" unresolved verbose format=json` results and
|
|
47
|
-
confirm each repaired target disappeared from the relevant source's findings.
|
|
48
|
-
If file contents and CLI results disagree, reload Obsidian with
|
|
49
|
-
`obsidian vault="$VAULT_NAME" command id="app:reload"` and retry after it loads.
|
|
50
|
-
Review fresh orphan results too; unrelated totals can change during sync.
|
|
37
|
+
Review the diff for intended link changes, then check fresh unresolved-link
|
|
38
|
+
results. If CLI results disagree with file contents, reload Obsidian with
|
|
39
|
+
`obsidian vault="$VAULT_NAME" command id="app:reload"` and retry.
|
|
51
40
|
|
|
52
41
|
## Orphans
|
|
53
42
|
|
|
@@ -64,15 +64,14 @@ accepts reviewed entries; it does not infer categories or descriptions:
|
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Pass reviewed entries as JSON:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
uv run
|
|
71
|
-
"$VAULT" wiki/topic/index.md < /tmp/index-review.json > /tmp/index-preview.md
|
|
70
|
+
uv run "$SCRIPTS/edit-index.py" wiki/topic/index.md --apply < entries.json
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
Omit `--apply` to preview. Supply `"title": "Topic"` instead of `section`
|
|
74
|
+
when creating a flat index. For an
|
|
76
75
|
existing sectioned index, `section` must identify one leaf heading exactly.
|
|
77
76
|
Multiple entry blocks, continuation text, and fenced examples are refused for
|
|
78
77
|
manual review.
|
|
@@ -81,7 +80,7 @@ Only the selected entry block is sorted: subfolder indexes first, then files
|
|
|
81
80
|
alphabetically. Existing matching entries are updated; other prose and sections
|
|
82
81
|
are preserved. Targets must exist and be visible. Protected sources, hidden
|
|
83
82
|
paths, symlinks, read-only paths, and paths outside managed zones are refused.
|
|
84
|
-
|
|
83
|
+
Applied writes use `obsidian-knowledge write`.
|
|
85
84
|
|
|
86
85
|
## Stale path-based wikilinks
|
|
87
86
|
|
|
@@ -94,11 +93,7 @@ prefix recovery and ambiguity rules.
|
|
|
94
93
|
Always use the Obsidian CLI to move files; never use filesystem `mv`:
|
|
95
94
|
|
|
96
95
|
```bash
|
|
97
|
-
obsidian vault="
|
|
96
|
+
obsidian vault="$VAULT_NAME" move path="old/path.md" to="new/folder/file.md" silent
|
|
98
97
|
```
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
exists within the configured filesystem root. CLI success text alone is
|
|
102
|
-
insufficient. Then search
|
|
103
|
-
the vault for the old filename to verify Obsidian updated all references. Fix any
|
|
104
|
-
stale wikilinks found.
|
|
99
|
+
Check the destination and repair any stale links left by the move.
|
|
@@ -22,14 +22,12 @@ there.
|
|
|
22
22
|
3. Choose a descriptive name using the vault's `CLAUDE.md` conventions. Prefer
|
|
23
23
|
dates from file content, then EXIF metadata, then the filename, then the
|
|
24
24
|
parent folder. Omit the date when none is reliable.
|
|
25
|
-
4. For a confident match,
|
|
25
|
+
4. For a confident match, rename the file:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
obsidian vault="$VAULT_NAME" rename path="old/name.ext" name="new-name.ext"
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
5.
|
|
32
|
-
is gone. If content changed intentionally (such as orientation correction),
|
|
33
|
-
take the rename baseline after that change. Search for the old name
|
|
31
|
+
5. Check the new path and search for the old name
|
|
34
32
|
and repair stale links. For low-confidence cases, add the proposed name and
|
|
35
33
|
reasoning to `needs-attention.md` instead of renaming.
|
|
@@ -18,10 +18,10 @@ Replace `NOTE_PATH` with the note's filesystem path:
|
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
# Dry run (reports what would change)
|
|
21
|
-
uv run
|
|
21
|
+
uv run "$SCRIPTS/fix-stacked-frontmatter.py" NOTE_PATH
|
|
22
22
|
|
|
23
23
|
# Apply the fix
|
|
24
|
-
uv run
|
|
24
|
+
uv run "$SCRIPTS/fix-stacked-frontmatter.py" --fix NOTE_PATH
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
The script reports these output codes:
|
obsidian_knowledge-3.26.3/plugins/obsidian-knowledge/skills/vault-organizer/organizer_context.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Configured vault defaults for standalone gardener scripts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "hooks"))
|
|
11
|
+
from vault_registry import load_vault_roots
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def resolve_vault(explicit: Path | None = None) -> Path:
|
|
15
|
+
"""Use an explicit root, the enclosing configured vault, or the sole vault."""
|
|
16
|
+
try:
|
|
17
|
+
if explicit is None:
|
|
18
|
+
roots = tuple(Path(root) for root in load_vault_roots())
|
|
19
|
+
cwd = Path.cwd().resolve()
|
|
20
|
+
explicit = next((root for root in roots if cwd.is_relative_to(root)), None)
|
|
21
|
+
if explicit is None:
|
|
22
|
+
if len(roots) != 1:
|
|
23
|
+
raise ValueError("pass a vault path when no single configured vault applies")
|
|
24
|
+
explicit = roots[0]
|
|
25
|
+
root = explicit.expanduser().resolve(strict=True)
|
|
26
|
+
if not root.is_dir():
|
|
27
|
+
raise ValueError(f"not a vault directory: {root}")
|
|
28
|
+
return root
|
|
29
|
+
except (OSError, ValueError, yaml.YAMLError) as exc:
|
|
30
|
+
raise SystemExit(f"Error: {exc}") from exc
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["pyyaml", "pydantic>=2"]
|
|
5
|
+
# ///
|
|
2
6
|
"""Classify and deterministically recover Obsidian unresolved wikilinks.
|
|
3
7
|
|
|
4
8
|
Usage:
|
|
5
9
|
obsidian unresolved verbose format=json \
|
|
6
|
-
|
|
|
10
|
+
| uv run recover-unresolved-links.py [vault_root] [--apply]
|
|
7
11
|
|
|
8
12
|
The script is intentionally conservative. It auto-fixes only unique exact
|
|
9
13
|
filename/alias/path recoveries. Ambiguous and fuzzy-looking items are reported
|
|
@@ -22,6 +26,7 @@ from collections.abc import Iterable
|
|
|
22
26
|
from difflib import SequenceMatcher
|
|
23
27
|
from pathlib import Path
|
|
24
28
|
|
|
29
|
+
import yaml as yaml_module
|
|
25
30
|
from link_recovery_models import (
|
|
26
31
|
DEFAULT_STUB_PATTERNS,
|
|
27
32
|
CandidateIndex,
|
|
@@ -31,16 +36,9 @@ from link_recovery_models import (
|
|
|
31
36
|
RecoveryDecision,
|
|
32
37
|
UnresolvedItem,
|
|
33
38
|
)
|
|
39
|
+
from organizer_context import resolve_vault
|
|
34
40
|
from organizer_io import OrganizerPolicy, visible_path, write_checked
|
|
35
41
|
|
|
36
|
-
try:
|
|
37
|
-
import yaml as yaml_module
|
|
38
|
-
|
|
39
|
-
HAS_YAML = True
|
|
40
|
-
except ImportError: # pragma: no cover - exercised in packaged fallback contexts
|
|
41
|
-
yaml_module = None
|
|
42
|
-
HAS_YAML = False
|
|
43
|
-
|
|
44
42
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
45
43
|
|
|
46
44
|
TEMPLATE_PLACEHOLDER = re.compile(r"\{\{|\<%")
|
|
@@ -50,7 +48,7 @@ WIKILINK_CHARS = set("|#^")
|
|
|
50
48
|
|
|
51
49
|
def parse_args() -> argparse.Namespace:
|
|
52
50
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
53
|
-
parser.add_argument("vault_root", type=Path)
|
|
51
|
+
parser.add_argument("vault_root", type=Path, nargs="?", help="default: configured vault")
|
|
54
52
|
parser.add_argument(
|
|
55
53
|
"--apply", action="store_true", help="rewrite exact/high-confidence recoveries in source files"
|
|
56
54
|
)
|
|
@@ -65,9 +63,8 @@ def parse_args() -> argparse.Namespace:
|
|
|
65
63
|
|
|
66
64
|
def load_config(vault_root: Path) -> Config:
|
|
67
65
|
config_path = vault_root / ".claude" / "obsidian-knowledge.yaml"
|
|
68
|
-
if
|
|
66
|
+
if config_path.exists():
|
|
69
67
|
with open(config_path, encoding="utf-8") as handle:
|
|
70
|
-
assert yaml_module is not None
|
|
71
68
|
raw = yaml_module.safe_load(handle) or {}
|
|
72
69
|
return Config(
|
|
73
70
|
ai_managed=tuple(str(zone) for zone in raw.get("ai_managed", ("wiki",))),
|
|
@@ -126,10 +123,9 @@ def frontmatter_aliases(text: str) -> tuple[str, ...]:
|
|
|
126
123
|
if not text.startswith("---\n"):
|
|
127
124
|
return ()
|
|
128
125
|
end = text.find("\n---", 4)
|
|
129
|
-
if end == -1
|
|
126
|
+
if end == -1:
|
|
130
127
|
return ()
|
|
131
128
|
try:
|
|
132
|
-
assert yaml_module is not None
|
|
133
129
|
frontmatter = yaml_module.safe_load(text[4:end]) or {}
|
|
134
130
|
except yaml_module.YAMLError:
|
|
135
131
|
return ()
|
|
@@ -390,7 +386,7 @@ def emit(decisions: list[RecoveryDecision], output_format: str) -> None:
|
|
|
390
386
|
|
|
391
387
|
def main() -> None:
|
|
392
388
|
args = parse_args()
|
|
393
|
-
vault_root = args.vault_root
|
|
389
|
+
vault_root = resolve_vault(args.vault_root)
|
|
394
390
|
config = load_config(vault_root)
|
|
395
391
|
patterns = tuple(re.compile(pattern) for pattern in config.stub_link_patterns)
|
|
396
392
|
raw_items = json.load(sys.stdin)
|