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.
Files changed (211) hide show
  1. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude-plugin/marketplace.json +1 -1
  2. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude-plugin/plugin.json +1 -1
  3. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.codex-plugin/plugin.json +1 -1
  4. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/PKG-INFO +1 -1
  5. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/.codex-plugin/plugin.json +1 -1
  6. obsidian_knowledge-3.26.3/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +98 -0
  7. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/convention-sweep.py +14 -32
  8. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/edit-index.py +9 -4
  9. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/filter-unresolved-links.py +14 -15
  10. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/find-open-questions.py +20 -24
  11. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
  12. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/broken-links.md +4 -15
  13. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/index-format.md +7 -12
  14. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/rename-files.md +2 -4
  15. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
  16. obsidian_knowledge-3.26.3/plugins/obsidian-knowledge/skills/vault-organizer/organizer_context.py +30 -0
  17. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/recover-unresolved-links.py +12 -16
  18. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3/plugins/obsidian-knowledge}/skills/vault-organizer/vault-audit.py +13 -18
  19. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/pyproject.toml +1 -1
  20. obsidian_knowledge-3.26.3/skills/vault-organizer/SKILL.md +98 -0
  21. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/convention-sweep.py +14 -32
  22. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/edit-index.py +9 -4
  23. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/filter-unresolved-links.py +14 -15
  24. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/find-open-questions.py +20 -24
  25. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/fix-stacked-frontmatter.py +7 -3
  26. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/broken-links.md +4 -15
  27. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/index-format.md +7 -12
  28. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/rename-files.md +2 -4
  29. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/stacked-frontmatter.md +2 -2
  30. obsidian_knowledge-3.26.3/skills/vault-organizer/organizer_context.py +30 -0
  31. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/recover-unresolved-links.py +12 -16
  32. {obsidian_knowledge-3.26.2/plugins/obsidian-knowledge → obsidian_knowledge-3.26.3}/skills/vault-organizer/vault-audit.py +13 -18
  33. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_gardener_helpers.py +78 -0
  34. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/uv.lock +1 -1
  35. obsidian_knowledge-3.26.2/plugins/obsidian-knowledge/skills/vault-organizer/SKILL.md +0 -166
  36. obsidian_knowledge-3.26.2/skills/vault-organizer/SKILL.md +0 -166
  37. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.agents/plugins/marketplace.json +0 -0
  38. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/agents.red-green-tdd.md +0 -0
  39. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.doc-code-coupling.md +0 -0
  40. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.no-junk-drawers.md +0 -0
  41. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.parse-dont-validate.md +0 -0
  42. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.require-plugin-version-bump.md +0 -0
  43. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.semantic-types.md +0 -0
  44. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.claude/hookify.taste-enforcer.md +0 -0
  45. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/ci.yml +0 -0
  46. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/docs.yml +0 -0
  47. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.github/workflows/publish.yml +0 -0
  48. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.gitignore +0 -0
  49. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/.secrets.baseline +0 -0
  50. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/AGENTS.md +0 -0
  51. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/CONVENTIONS.md +0 -0
  52. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/LICENSE +0 -0
  53. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/README.md +0 -0
  54. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/commands/scan-secrets.md +0 -0
  55. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/.pages +0 -0
  56. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/ARCHITECTURE.md +0 -0
  57. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/CLI.md +0 -0
  58. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/MCP.md +0 -0
  59. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/QUALITY.md +0 -0
  60. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/TODO.md +0 -0
  61. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/configuration.md +0 -0
  62. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/contributing-docs.md +0 -0
  63. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/hooks.md +0 -0
  64. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/index.md +0 -0
  65. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/plans/2026-04-25-improve-harness-implementation.md +0 -0
  66. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/plans/2026-05-12-changelog-reform.md +0 -0
  67. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/specs/2026-04-25-improve-harness-design.md +0 -0
  68. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/docs/superpowers/specs/2026-05-12-changelog-reform-design.md +0 -0
  69. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/capture-session.py +0 -0
  70. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/codex-hooks.json +0 -0
  71. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/doctor.py +0 -0
  72. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/__init__.py +0 -0
  73. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/capture.py +0 -0
  74. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/patterns.py +0 -0
  75. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/recall_init_lib.py +0 -0
  76. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/reflect_counter.py +0 -0
  77. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/repo_memory.py +0 -0
  78. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/secret_filters.py +0 -0
  79. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/shell_guard.py +0 -0
  80. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/stop_hook.py +0 -0
  81. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/transcript.py +0 -0
  82. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/vault_config.py +0 -0
  83. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/hookslib/vault_policy.py +0 -0
  84. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/i-insist.toml +0 -0
  85. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/i_insist.py +0 -0
  86. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/nudge-index-sync.py +0 -0
  87. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/recall-init.py +0 -0
  88. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/reflect-nudge.py +0 -0
  89. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/remind-convos.py +0 -0
  90. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/remind-convos.sh +0 -0
  91. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/scan-vault-secrets.py +0 -0
  92. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/update-changelog.py +0 -0
  93. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/update-changelog.sh +0 -0
  94. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/hooks/vault_registry.py +0 -0
  95. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/__init__.py +0 -0
  96. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/__init__.py +0 -0
  97. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/cli.py +0 -0
  98. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/config.py +0 -0
  99. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/filters.py +0 -0
  100. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/guard_install.py +0 -0
  101. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/indexer.py +0 -0
  102. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/mcp_server.py +0 -0
  103. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/models.py +0 -0
  104. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/papercuts.py +0 -0
  105. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/primer.py +0 -0
  106. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/registration.py +0 -0
  107. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/lib/vault_index/vault_files.py +0 -0
  108. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/mkdocs.yml +0 -0
  109. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/commands/scan-secrets.md +0 -0
  110. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/capture-session.py +0 -0
  111. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/doctor.py +0 -0
  112. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hooks.json +0 -0
  113. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/__init__.py +0 -0
  114. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/capture.py +0 -0
  115. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/patterns.py +0 -0
  116. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/recall_init_lib.py +0 -0
  117. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/reflect_counter.py +0 -0
  118. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/repo_memory.py +0 -0
  119. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/secret_filters.py +0 -0
  120. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/shell_guard.py +0 -0
  121. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/stop_hook.py +0 -0
  122. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/transcript.py +0 -0
  123. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/vault_config.py +0 -0
  124. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/hookslib/vault_policy.py +0 -0
  125. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/i-insist.toml +0 -0
  126. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/i_insist.py +0 -0
  127. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/nudge-index-sync.py +0 -0
  128. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/recall-init.py +0 -0
  129. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/reflect-nudge.py +0 -0
  130. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/remind-convos.py +0 -0
  131. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/remind-convos.sh +0 -0
  132. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/scan-vault-secrets.py +0 -0
  133. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/update-changelog.py +0 -0
  134. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/update-changelog.sh +0 -0
  135. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/hooks/vault_registry.py +0 -0
  136. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/obsidian-knowledge/SKILL.md +0 -0
  137. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/remember-conversations/SKILL.md +0 -0
  138. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/note-types.md +0 -0
  139. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/lib/state-files.md +0 -0
  140. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/link_recovery_models.py +0 -0
  141. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/plugins/obsidian-knowledge/skills/vault-organizer/organizer_io.py +0 -0
  142. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/prek.toml +0 -0
  143. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/build_memory_indexes.py +0 -0
  144. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/cli_smoke_test.py +0 -0
  145. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/migrate_changelog.py +0 -0
  146. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/migrate_claude_memory.py +0 -0
  147. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/new-worktree.sh +0 -0
  148. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_architecture.py +0 -0
  149. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_exception_handling.py +0 -0
  150. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_file_length.py +0 -0
  151. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_private_test_imports.py +0 -0
  152. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prek_hooks/check_timeless_comments.py +0 -0
  153. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/prepare_release.py +0 -0
  154. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/smoke_tool_install.py +0 -0
  155. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/scripts/sync_codex_plugin.py +0 -0
  156. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/obsidian-knowledge/SKILL.md +0 -0
  157. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/remember-conversations/SKILL.md +0 -0
  158. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/note-types.md +0 -0
  159. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/lib/state-files.md +0 -0
  160. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/link_recovery_models.py +0 -0
  161. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/skills/vault-organizer/organizer_io.py +0 -0
  162. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/__init__.py +0 -0
  163. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/conftest.py +0 -0
  164. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/.claude/obsidian-knowledge.yaml +0 -0
  165. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/Inbox/random.md +0 -0
  166. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/Journal/diary.md +0 -0
  167. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/wiki/python.md +0 -0
  168. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/sample_vault/wiki/rust.md +0 -0
  169. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/transcript_new_file_no_index.jsonl +0 -0
  170. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/fixtures/transcript_new_file_with_index.jsonl +0 -0
  171. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_access_errors.py +0 -0
  172. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_architecture_check.py +0 -0
  173. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_capture_instructions.py +0 -0
  174. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_capture_session_hook.py +0 -0
  175. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli.py +0 -0
  176. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_dispatch.py +0 -0
  177. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_reliability.py +0 -0
  178. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_cli_smoke.py +0 -0
  179. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_config.py +0 -0
  180. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_doctor.py +0 -0
  181. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_filters.py +0 -0
  182. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_first_install.py +0 -0
  183. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_i_insist_rules.py +0 -0
  184. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_indexer.py +0 -0
  185. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_indexer_reliability.py +0 -0
  186. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_mcp.py +0 -0
  187. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_mcp_download.py +0 -0
  188. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_migrate_changelog.py +0 -0
  189. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_nudge_index_sync.py +0 -0
  190. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_papercut_cli.py +0 -0
  191. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_papercuts.py +0 -0
  192. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_patterns.py +0 -0
  193. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_primer.py +0 -0
  194. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_protect_vault_destructive_ops.py +0 -0
  195. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_public_api.py +0 -0
  196. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recall_init.py +0 -0
  197. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recall_init_lib.py +0 -0
  198. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_recover_unresolved_links.py +0 -0
  199. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_reflect_counter.py +0 -0
  200. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_reflect_nudge.py +0 -0
  201. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_release.py +0 -0
  202. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_repo_memory.py +0 -0
  203. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_scan_vault_secrets.py +0 -0
  204. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_search_json.py +0 -0
  205. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_secret_scan_filters.py +0 -0
  206. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_session_debounce.py +0 -0
  207. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_transcript.py +0 -0
  208. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_boundaries.py +0 -0
  209. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_files.py +0 -0
  210. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/tests/test_vault_policy.py +0 -0
  211. {obsidian_knowledge-3.26.2 → obsidian_knowledge-3.26.3}/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.3"
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.3",
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.3",
4
4
  "description": "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
  Metadata-Version: 2.5
2
2
  Name: obsidian-knowledge
3
- Version: 3.26.2
3
+ Version: 3.26.3
4
4
  Summary: Obsidian vault knowledge integration for Claude Code and Codex
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-knowledge",
3
- "version": "3.26.2+codex.20261003154117",
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 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
 
@@ -64,15 +64,14 @@ accepts reviewed entries; it does not infer categories or descriptions:
64
64
  }
65
65
  ```
66
66
 
67
- Save the review as JSON, then render without writing:
67
+ Pass reviewed entries as JSON:
68
68
 
69
69
  ```bash
70
- uv run --no-project --with pyyaml --with pydantic python "$SCRIPTS/edit-index.py" \
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
- Inspect the diff against the current index, then repeat with `--apply`. Supply
75
- `"title": "Topic"` instead of `section` when creating a flat index. For an
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
- All applied writes use `obsidian-knowledge write` and verify final bytes.
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="<vault>" move path="old/path.md" to="new/folder/file.md" silent
96
+ obsidian vault="$VAULT_NAME" move path="old/path.md" to="new/folder/file.md" silent
98
97
  ```
99
98
 
100
- After each move, verify that the new path exists and the old path no longer
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, record the original file's SHA-256, then rename it:
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. Verify the new path exists, its SHA-256 matches the original, and the old path
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 --no-project --with pyyaml python "$SCRIPTS/fix-stacked-frontmatter.py" NOTE_PATH
21
+ uv run "$SCRIPTS/fix-stacked-frontmatter.py" NOTE_PATH
22
22
 
23
23
  # Apply the fix
24
- uv run --no-project --with pyyaml python "$SCRIPTS/fix-stacked-frontmatter.py" --fix NOTE_PATH
24
+ uv run "$SCRIPTS/fix-stacked-frontmatter.py" --fix NOTE_PATH
25
25
  ```
26
26
 
27
27
  The script reports these output codes:
@@ -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 python3
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
- | python3 recover-unresolved-links.py <vault_root> [--apply]
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 HAS_YAML and config_path.exists():
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 or not HAS_YAML:
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.resolve()
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)