obsidian-knowledge 3.26.2__tar.gz → 3.26.4__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.
Files changed (213) hide show
  1. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude-plugin/marketplace.json +1 -1
  2. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude-plugin/plugin.json +1 -1
  3. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.codex-plugin/plugin.json +1 -1
  4. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/PKG-INFO +2 -1
  5. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/ARCHITECTURE.md +6 -0
  6. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/indexer.py +7 -16
  7. obsidian_knowledge-3.26.4/lib/vault_index/vector_search.py +104 -0
  8. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/.codex-plugin/plugin.json +1 -1
  9. obsidian_knowledge-3.26.4/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +98 -0
  10. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/convention-sweep.py +14 -32
  11. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/edit-index.py +9 -4
  12. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/filter-unresolved-links.py +14 -15
  13. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/find-open-questions.py +20 -24
  14. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
  15. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/lib/broken-links.md +4 -15
  16. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/lib/index-format.md +7 -12
  17. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/lib/rename-files.md +2 -4
  18. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
  19. obsidian_knowledge-3.26.4/plugins/obsidian-knowledge/skills/vault-organizer/organizer_context.py +30 -0
  20. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/recover-unresolved-links.py +12 -16
  21. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4/plugins/obsidian-knowledge}/skills/vault-organizer/vault-audit.py +13 -18
  22. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/pyproject.toml +2 -1
  23. obsidian_knowledge-3.26.4/skills/vault-organizer/SKILL.md +98 -0
  24. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/convention-sweep.py +14 -32
  25. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/edit-index.py +9 -4
  26. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/filter-unresolved-links.py +14 -15
  27. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/find-open-questions.py +20 -24
  28. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
  29. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/broken-links.md +4 -15
  30. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/index-format.md +7 -12
  31. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/rename-files.md +2 -4
  32. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
  33. obsidian_knowledge-3.26.4/skills/vault-organizer/organizer_context.py +30 -0
  34. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/recover-unresolved-links.py +12 -16
  35. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.4}/skills/vault-organizer/vault-audit.py +13 -18
  36. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_gardener_helpers.py +78 -0
  37. obsidian_knowledge-3.26.4/tests/test_vector_latency.py +176 -0
  38. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/uv.lock +4 -2
  39. obsidian_knowledge-3.26.2/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +0 -166
  40. obsidian_knowledge-3.26.2/skills/vault-organizer/SKILL.md +0 -166
  41. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.agents/plugins/marketplace.json +0 -0
  42. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/agents.red-green-tdd.md +0 -0
  43. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.doc-code-coupling.md +0 -0
  44. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.no-junk-drawers.md +0 -0
  45. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.parse-dont-validate.md +0 -0
  46. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.require-plugin-version-bump.md +0 -0
  47. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.semantic-types.md +0 -0
  48. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.claude/hookify.taste-enforcer.md +0 -0
  49. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.github/workflows/ci.yml +0 -0
  50. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.github/workflows/docs.yml +0 -0
  51. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.github/workflows/publish.yml +0 -0
  52. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.gitignore +0 -0
  53. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/.secrets.baseline +0 -0
  54. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/AGENTS.md +0 -0
  55. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/CONVENTIONS.md +0 -0
  56. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/LICENSE +0 -0
  57. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/README.md +0 -0
  58. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/commands/scan-secrets.md +0 -0
  59. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/.pages +0 -0
  60. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/CLI.md +0 -0
  61. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/MCP.md +0 -0
  62. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/QUALITY.md +0 -0
  63. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/TODO.md +0 -0
  64. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/configuration.md +0 -0
  65. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/contributing-docs.md +0 -0
  66. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/hooks.md +0 -0
  67. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/index.md +0 -0
  68. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/superpowers/plans/2026-04-25-improve-harness-implementation.md +0 -0
  69. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/superpowers/plans/2026-05-12-changelog-reform.md +0 -0
  70. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/superpowers/specs/2026-04-25-improve-harness-design.md +0 -0
  71. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/docs/superpowers/specs/2026-05-12-changelog-reform-design.md +0 -0
  72. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/capture-session.py +0 -0
  73. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/codex-hooks.json +0 -0
  74. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/doctor.py +0 -0
  75. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/__init__.py +0 -0
  76. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/capture.py +0 -0
  77. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/patterns.py +0 -0
  78. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/recall_init_lib.py +0 -0
  79. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/reflect_counter.py +0 -0
  80. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/repo_memory.py +0 -0
  81. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/secret_filters.py +0 -0
  82. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/shell_guard.py +0 -0
  83. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/stop_hook.py +0 -0
  84. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/transcript.py +0 -0
  85. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/vault_config.py +0 -0
  86. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/hookslib/vault_policy.py +0 -0
  87. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/i-insist.toml +0 -0
  88. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/i_insist.py +0 -0
  89. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/nudge-index-sync.py +0 -0
  90. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/recall-init.py +0 -0
  91. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/reflect-nudge.py +0 -0
  92. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/remind-convos.py +0 -0
  93. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/remind-convos.sh +0 -0
  94. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/scan-vault-secrets.py +0 -0
  95. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/update-changelog.py +0 -0
  96. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/update-changelog.sh +0 -0
  97. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/hooks/vault_registry.py +0 -0
  98. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/__init__.py +0 -0
  99. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/__init__.py +0 -0
  100. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/cli.py +0 -0
  101. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/config.py +0 -0
  102. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/filters.py +0 -0
  103. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/guard_install.py +0 -0
  104. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/mcp_server.py +0 -0
  105. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/models.py +0 -0
  106. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/papercuts.py +0 -0
  107. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/primer.py +0 -0
  108. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/registration.py +0 -0
  109. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/lib/vault_index/vault_files.py +0 -0
  110. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/mkdocs.yml +0 -0
  111. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/commands/scan-secrets.md +0 -0
  112. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/capture-session.py +0 -0
  113. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/doctor.py +0 -0
  114. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hooks.json +0 -0
  115. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/__init__.py +0 -0
  116. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/capture.py +0 -0
  117. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/patterns.py +0 -0
  118. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/recall_init_lib.py +0 -0
  119. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/reflect_counter.py +0 -0
  120. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/repo_memory.py +0 -0
  121. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/secret_filters.py +0 -0
  122. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/shell_guard.py +0 -0
  123. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/stop_hook.py +0 -0
  124. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/transcript.py +0 -0
  125. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/vault_config.py +0 -0
  126. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/hookslib/vault_policy.py +0 -0
  127. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/i-insist.toml +0 -0
  128. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/i_insist.py +0 -0
  129. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/nudge-index-sync.py +0 -0
  130. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/recall-init.py +0 -0
  131. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/reflect-nudge.py +0 -0
  132. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/remind-convos.py +0 -0
  133. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/remind-convos.sh +0 -0
  134. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/scan-vault-secrets.py +0 -0
  135. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/update-changelog.py +0 -0
  136. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/update-changelog.sh +0 -0
  137. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/hooks/vault_registry.py +0 -0
  138. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/obsidian-knowledge/SKILL.md +0 -0
  139. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/remember-conversations/SKILL.md +0 -0
  140. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/lib/note-types.md +0 -0
  141. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/lib/state-files.md +0 -0
  142. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/link_recovery_models.py +0 -0
  143. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/plugins/obsidian-knowledge/skills/vault-organizer/organizer_io.py +0 -0
  144. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/prek.toml +0 -0
  145. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/build_memory_indexes.py +0 -0
  146. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/cli_smoke_test.py +0 -0
  147. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/migrate_changelog.py +0 -0
  148. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/migrate_claude_memory.py +0 -0
  149. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/new-worktree.sh +0 -0
  150. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prek_hooks/check_architecture.py +0 -0
  151. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prek_hooks/check_exception_handling.py +0 -0
  152. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prek_hooks/check_file_length.py +0 -0
  153. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prek_hooks/check_private_test_imports.py +0 -0
  154. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prek_hooks/check_timeless_comments.py +0 -0
  155. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/prepare_release.py +0 -0
  156. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/smoke_tool_install.py +0 -0
  157. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/scripts/sync_codex_plugin.py +0 -0
  158. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/obsidian-knowledge/SKILL.md +0 -0
  159. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/remember-conversations/SKILL.md +0 -0
  160. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/note-types.md +0 -0
  161. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/lib/state-files.md +0 -0
  162. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/link_recovery_models.py +0 -0
  163. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/skills/vault-organizer/organizer_io.py +0 -0
  164. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/__init__.py +0 -0
  165. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/conftest.py +0 -0
  166. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/sample_vault/.claude/obsidian-knowledge.yaml +0 -0
  167. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/sample_vault/Inbox/random.md +0 -0
  168. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/sample_vault/Journal/diary.md +0 -0
  169. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/sample_vault/wiki/python.md +0 -0
  170. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/sample_vault/wiki/rust.md +0 -0
  171. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/transcript_new_file_no_index.jsonl +0 -0
  172. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/fixtures/transcript_new_file_with_index.jsonl +0 -0
  173. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_access_errors.py +0 -0
  174. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_architecture_check.py +0 -0
  175. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_capture_instructions.py +0 -0
  176. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_capture_session_hook.py +0 -0
  177. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_cli.py +0 -0
  178. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_cli_dispatch.py +0 -0
  179. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_cli_reliability.py +0 -0
  180. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_cli_smoke.py +0 -0
  181. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_config.py +0 -0
  182. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_doctor.py +0 -0
  183. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_filters.py +0 -0
  184. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_first_install.py +0 -0
  185. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_i_insist_rules.py +0 -0
  186. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_indexer.py +0 -0
  187. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_indexer_reliability.py +0 -0
  188. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_mcp.py +0 -0
  189. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_mcp_download.py +0 -0
  190. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_migrate_changelog.py +0 -0
  191. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_nudge_index_sync.py +0 -0
  192. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_papercut_cli.py +0 -0
  193. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_papercuts.py +0 -0
  194. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_patterns.py +0 -0
  195. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_primer.py +0 -0
  196. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_protect_vault_destructive_ops.py +0 -0
  197. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_public_api.py +0 -0
  198. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_recall_init.py +0 -0
  199. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_recall_init_lib.py +0 -0
  200. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_recover_unresolved_links.py +0 -0
  201. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_reflect_counter.py +0 -0
  202. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_reflect_nudge.py +0 -0
  203. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_release.py +0 -0
  204. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_repo_memory.py +0 -0
  205. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_scan_vault_secrets.py +0 -0
  206. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_search_json.py +0 -0
  207. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_secret_scan_filters.py +0 -0
  208. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_session_debounce.py +0 -0
  209. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_transcript.py +0 -0
  210. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_vault_boundaries.py +0 -0
  211. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_vault_files.py +0 -0
  212. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_vault_policy.py +0 -0
  213. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.4}/tests/test_vault_registry.py +0 -0
@@ -11,7 +11,7 @@
11
11
  "name": "obsidian-knowledge",
12
12
  "source": "./",
13
13
  "description": "Skills, hooks, and conventions for maintaining Obsidian knowledge base vaults",
14
- "version": "3.26.2"
14
+ "version": "3.26.4"
15
15
  }
16
16
  ]
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-knowledge",
3
- "version": "3.26.2",
3
+ "version": "3.26.4",
4
4
  "description": "Claude Code plugin that manages an Obsidian knowledge vault: capture, organize, index, and garden",
5
5
  "author": {
6
6
  "name": "Ricardo Decal"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-knowledge",
3
- "version": "3.26.2",
3
+ "version": "3.26.4",
4
4
  "description": "Plugin that manages an Obsidian knowledge vault: capture, organize, index, and garden.",
5
5
  "author": {
6
6
  "name": "Ricardo Decal"
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: obsidian-knowledge
3
- Version: 3.26.2
3
+ Version: 3.26.4
4
4
  Summary: Obsidian vault knowledge integration for Claude Code and Codex
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
+ Requires-Dist: aiosqlite<1,>=0.20
7
8
  Requires-Dist: beartype>=0.22.9
8
9
  Requires-Dist: botocore>=1.43.10
9
10
  Requires-Dist: httpx[socks]>=0.28.1
@@ -22,6 +22,12 @@ weights, and `indexer.py` wraps memweave's keyword and dense retrieval.
22
22
  file operations and bounded CLI search subprocesses over Streamable HTTP. Importing `lib` enables
23
23
  beartype runtime checks.
24
24
 
25
+ `vector_search.py` registers a memweave hybrid strategy using sqlite-vec's native
26
+ nearest-neighbor query. Memweave normalizes embeddings, so L2 candidate ordering
27
+ matches cosine ordering; the strategy keeps the original cosine scores and
28
+ keyword merge. Model and source filters apply before candidate selection. Pools
29
+ above sqlite-vec's 4096-candidate ceiling retain memweave's exhaustive search.
30
+
25
31
  The wheel installs `hooks/vault_registry.py` as the top-level `vault_registry`
26
32
  module so hooks and the CLI share one registry implementation.
27
33
 
@@ -63,6 +63,7 @@ import platformdirs
63
63
  from lib.vault_index.config import VaultIndexConfig
64
64
  from lib.vault_index.filters import path_passes
65
65
  from lib.vault_index.models import Hit, IndexBusyError, SearchReport
66
+ from lib.vault_index.vector_search import IndexedHybridSearch, KeywordOnlyEmbeddingProvider
66
67
 
67
68
  # Local embeddings do not need a public model-pricing download. LiteLLM reads
68
69
  # this at its first lazy import; preserve an explicit operator override.
@@ -153,20 +154,6 @@ class SyncStats:
153
154
  deleted: int
154
155
 
155
156
 
156
- class KeywordOnlyEmbeddingProvider:
157
- """Keep memweave's indexing pipeline offline when vectors are disabled.
158
-
159
- memweave 0.2 still calls its provider with vector.enabled=False. Empty
160
- vectors retain chunks and FTS entries without caching fake embeddings.
161
- """
162
-
163
- async def embed_query(self, text: str) -> list[float]:
164
- return []
165
-
166
- async def embed_batch(self, texts: list[str]) -> list[list[float]]:
167
- return [[] for _ in texts]
168
-
169
-
170
157
  @contextlib.contextmanager
171
158
  def index_lock(cache_dir: Path, *, exclusive: bool, blocking: bool = False):
172
159
  """Hold the per-vault SQLite access lock.
@@ -261,10 +248,14 @@ class Indexer:
261
248
  return self._vector_enabled
262
249
 
263
250
  def _make_store(self, extra_paths: list[str]) -> memweave.MemWeave:
264
- return memweave.MemWeave(
265
- self._make_config(extra_paths=extra_paths),
251
+ config = self._make_config(extra_paths=extra_paths)
252
+ store = memweave.MemWeave(
253
+ config,
266
254
  embedding_provider=None if self._vector_enabled else KeywordOnlyEmbeddingProvider(),
267
255
  )
256
+ if self._vector_enabled:
257
+ store.register_strategy("hybrid", IndexedHybridSearch(config.query.hybrid))
258
+ return store
268
259
 
269
260
  def _embedder_fingerprint(self) -> str:
270
261
  """Stable string identifying the current embedding setup.
@@ -0,0 +1,104 @@
1
+ """Use sqlite-vec's native nearest-neighbor scan for hybrid retrieval."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlite3
6
+ import struct
7
+
8
+ import aiosqlite
9
+ from memweave import HybridConfig
10
+ from memweave.search.hybrid import HybridSearch, merge_hybrid_results
11
+ from memweave.search.keyword import KeywordSearch
12
+ from memweave.search.strategy import RawSearchRow
13
+
14
+
15
+ class IndexedHybridSearch:
16
+ """Preserve memweave's hybrid scoring without fetching every vector into SQL."""
17
+
18
+ def __init__(self, config: HybridConfig):
19
+ self.config = config
20
+
21
+ async def search(
22
+ self,
23
+ db: aiosqlite.Connection,
24
+ query: str,
25
+ query_vec: list[float] | None,
26
+ model: str,
27
+ limit: int,
28
+ *,
29
+ source_filter: str | None = None,
30
+ ) -> list[RawSearchRow]:
31
+ keyword = KeywordSearch()
32
+ if query_vec is None:
33
+ try:
34
+ await db.execute("SELECT vec_version()")
35
+ except sqlite3.OperationalError as exc:
36
+ if "no such function" not in str(exc):
37
+ raise
38
+ return await keyword.search(db, query, None, model, limit, source_filter=source_filter)
39
+ raise ValueError("VectorSearch requires a query_vec (got None)")
40
+
41
+ # NOTE: docs/ARCHITECTURE.md documents normalization and the k ceiling.
42
+ # Match HybridSearch's backend pool. sqlite-vec caps native k at 4096;
43
+ # unusually large requests retain memweave's exhaustive behavior.
44
+ fallback = HybridSearch(vector_weight=self.config.vector_weight, text_weight=self.config.text_weight)
45
+ pool = limit * fallback.candidate_multiplier
46
+ if pool > 4096:
47
+ return await fallback.search(db, query, query_vec, model, limit, source_filter=source_filter)
48
+
49
+ blob = struct.pack(f"<{len(query_vec)}f", *query_vec)
50
+ params: list[object] = [blob, pool, model, source_filter or None, source_filter or None, blob]
51
+ # memweave normalizes stored and query vectors: L2 and cosine rank
52
+ # identically. Compute original cosine scores on the bounded pool.
53
+ # Filter inside KNN so other models/sources cannot exhaust its pool.
54
+ sql = """
55
+ WITH nearest AS MATERIALIZED (
56
+ SELECT id, embedding, distance FROM chunks_vec
57
+ WHERE embedding MATCH ? AND k = ?
58
+ AND id IN (
59
+ SELECT id FROM chunks WHERE model = ? AND (? IS NULL OR source = ?)
60
+ )
61
+ )
62
+ SELECT c.id, c.path, c.source, c.start_line, c.end_line, c.text,
63
+ 1 - vec_distance_cosine(n.embedding, ?) AS score
64
+ FROM nearest n JOIN chunks c ON c.id = n.id
65
+ ORDER BY score DESC
66
+ """
67
+ vector_rows = []
68
+ async with db.execute(sql, params) as cursor:
69
+ async for row in cursor:
70
+ chunk_id, path, source, start_line, end_line, text, score = row
71
+ vector_rows.append(
72
+ RawSearchRow(
73
+ chunk_id=chunk_id,
74
+ path=path,
75
+ source=source,
76
+ start_line=start_line,
77
+ end_line=end_line,
78
+ text=text,
79
+ score=score,
80
+ vector_score=score,
81
+ )
82
+ )
83
+ keyword_rows = await keyword.search(db, query, None, model, pool, source_filter=source_filter)
84
+ return merge_hybrid_results(
85
+ vector_rows,
86
+ keyword_rows,
87
+ vector_weight=self.config.vector_weight,
88
+ text_weight=self.config.text_weight,
89
+ limit=limit,
90
+ )
91
+
92
+
93
+ class KeywordOnlyEmbeddingProvider:
94
+ """Keep memweave's indexing pipeline offline when vectors are disabled.
95
+
96
+ memweave 0.2 still calls its provider with vector.enabled=False. Empty
97
+ vectors retain chunks and FTS entries without caching fake embeddings.
98
+ """
99
+
100
+ async def embed_query(self, text: str) -> list[float]:
101
+ return []
102
+
103
+ async def embed_batch(self, texts: list[str]) -> list[list[float]]:
104
+ return [[] for _ in texts]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-knowledge",
3
- "version": "3.26.2+codex.20261003154117",
3
+ "version": "3.26.4+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 python3
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: python3 convention-sweep.py <vault_root>
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 sys
29
+ import argparse
26
30
  from pathlib import Path
27
31
 
32
+ from organizer_context import resolve_vault
28
33
 
29
- # Import shared patterns module from hooks/lib/. Installed plugin layout is:
30
- # <plugin>/hooks/hookslib/patterns.py
31
- # <plugin>/skills/vault-organizer/convention-sweep.py
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
- if len(sys.argv) < 2:
115
- print(f"Usage: {sys.argv[0]} <vault_root>", file=sys.stderr)
116
- sys.exit(1)
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 python3
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.resolve(strict=True)
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 python3
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 | python3 filter-unresolved-links.py <vault_root>
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 HAS_YAML and config_path.exists():
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
- if len(sys.argv) < 2:
77
- print(f"Usage: {sys.argv[0]} <vault_root>", file=sys.stderr)
78
- sys.exit(1)
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 python3
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: python3 find-open-questions.py <vault_root>
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 Utility/obsidian-knowledge/reports/open-questions.md to render
13
- Markdown for review, --timestamp for reproducible output, and --apply to write.
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 HAS_YAML and config_path.exists():
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 existing open-questions report (dry run)")
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 and not args.report:
141
- parser.error("--apply requires --report")
142
- vault_root = args.vault_root.resolve()
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 != "Utility/obsidian-knowledge/reports/open-questions.md":
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 python3
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
- python3 fix-stacked-frontmatter.py <file> [<file>...] # dry run
14
- python3 fix-stacked-frontmatter.py --fix <file> [<file>...] # rewrite
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
- Use the vault and script paths set in the organizer skill:
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
- Before repair, retain exact source bytes as a baseline. After repair, compare
44
- the diff and confirm only intended link targets changed. Primary prose, aliases,
45
- headings, block references, and frontmatter must remain intact. Then obtain
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