boost-skill-cli 1.0.151__tar.gz → 1.0.153__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 (315) hide show
  1. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost +6 -0
  3. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/_version.py +2 -2
  4. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/cli.py +1 -0
  5. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/bmad.py +5 -5
  6. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/configuration.py +5 -5
  7. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/discovery.py +3 -3
  8. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/intelligence.py +5 -5
  9. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/pkg.py +8 -15
  10. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/quality.py +2 -2
  11. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/taps.py +1 -1
  12. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/team.py +7 -7
  13. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/catalog.py +4 -4
  14. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/claude_settings.py +3 -3
  15. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/config.py +2 -2
  16. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/dense.py +12 -4
  17. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/journal.py +2 -2
  18. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/lockfile.py +3 -3
  19. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/output.py +17 -0
  20. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/paths.py +6 -2
  21. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/policy.py +2 -2
  22. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/rag.py +2 -2
  23. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/registry.py +7 -5
  24. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/serve.py +6 -1
  25. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/store.py +1 -1
  26. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/updatediff.py +1 -1
  27. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/util.py +24 -1
  28. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  29. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/windows-in-the-ci-matrix.md +3 -3
  30. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap.html +1 -1
  31. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/build_registries.py +2 -2
  32. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/build_roadmap.py +5 -5
  33. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/eval_retrieval.py +2 -2
  34. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/mutation_gate.py +1 -1
  35. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/discovery_steps.py +2 -2
  36. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_adapt.py +1 -1
  37. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_bmad.py +2 -2
  38. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_configuration.py +15 -15
  39. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_discovery.py +50 -24
  40. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_info.py +18 -10
  41. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_intelligence.py +23 -23
  42. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_pkg.py +23 -23
  43. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_quality.py +10 -10
  44. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_taps.py +9 -9
  45. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_team.py +12 -9
  46. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_everyday_loop.py +2 -2
  47. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/make_fixture.py +4 -2
  48. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_catalog.py +38 -33
  49. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_claude_settings.py +3 -3
  50. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_config.py +9 -9
  51. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_dense.py +38 -3
  52. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_docs_pages_linked.py +1 -1
  53. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_gitutil.py +3 -3
  54. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_journal.py +9 -9
  55. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_lockfile.py +14 -14
  56. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_logs.py +11 -11
  57. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_mutation_hardening.py +3 -3
  58. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_paths.py +6 -4
  59. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_policy.py +4 -4
  60. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_rag.py +15 -15
  61. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_registries_fresh.py +1 -1
  62. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_registry.py +6 -7
  63. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_roadmap_fresh.py +2 -2
  64. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_serve.py +51 -0
  65. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_store.py +27 -27
  66. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_token_parity.py +1 -1
  67. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_util.py +10 -10
  68. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/.gitattributes +0 -0
  69. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/.gitignore +0 -0
  70. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/CLAUDE.md +0 -0
  71. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/CONTRIBUTING.md +0 -0
  72. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/LICENSE +0 -0
  73. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/MANIFEST.in +0 -0
  74. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/Makefile +0 -0
  75. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/README.md +0 -0
  76. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/SECURITY.md +0 -0
  77. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/__init__.py +0 -0
  78. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/__main__.py +0 -0
  79. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/cliparse.py +0 -0
  80. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/__init__.py +0 -0
  81. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/hooks.py +0 -0
  82. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/commands/info.py +0 -0
  83. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/__init__.py +0 -0
  84. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/adapters.py +0 -0
  85. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/agents.py +0 -0
  86. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/ai.py +0 -0
  87. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/embed.py +0 -0
  88. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/frontmatter.py +0 -0
  89. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/gitutil.py +0 -0
  90. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/imperative.py +0 -0
  91. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/injectscan.py +0 -0
  92. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/logs.py +0 -0
  93. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/mcp.py +0 -0
  94. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/nethttp.py +0 -0
  95. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/rules.py +0 -0
  96. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/secretscan.py +0 -0
  97. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/stackprobe.py +0 -0
  98. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/staleness.py +0 -0
  99. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/typosquat.py +0 -0
  100. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/core/workflows.py +0 -0
  101. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/data/registries.json +0 -0
  102. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/errors.py +0 -0
  103. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_cli/spin.py +0 -0
  104. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
  105. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  106. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  107. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/requires.txt +0 -0
  108. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/top_level.txt +0 -0
  109. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/.claude/settings.local.json +0 -0
  110. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/DEBUGGING.md +0 -0
  111. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/adapters.html +0 -0
  112. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/demo.gif +0 -0
  113. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/demo.tape +0 -0
  114. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/design-roadmap.html +0 -0
  115. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/eval.html +0 -0
  116. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/index.html +0 -0
  117. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/mcp-hub.html +0 -0
  118. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/rag-architecture.md +0 -0
  119. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D01.md +0 -0
  120. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D02.md +0 -0
  121. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D03.md +0 -0
  122. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D04.md +0 -0
  123. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D05.md +0 -0
  124. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D06.md +0 -0
  125. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D07.md +0 -0
  126. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D08.md +0 -0
  127. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D09.md +0 -0
  128. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D10.md +0 -0
  129. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D11.md +0 -0
  130. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D12.md +0 -0
  131. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D13.md +0 -0
  132. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D14.md +0 -0
  133. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D15.md +0 -0
  134. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D16.md +0 -0
  135. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D17.md +0 -0
  136. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D18.md +0 -0
  137. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D19.md +0 -0
  138. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D20.md +0 -0
  139. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D21.md +0 -0
  140. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D22.md +0 -0
  141. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D23.md +0 -0
  142. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D24.md +0 -0
  143. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D25.md +0 -0
  144. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D26.md +0 -0
  145. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  146. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  147. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  148. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  149. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  150. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  151. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  152. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  153. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  154. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  155. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  156. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  157. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  158. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  159. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  160. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  161. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  162. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  163. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  164. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  165. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  166. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  167. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  168. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  169. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  170. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  171. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  172. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  173. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  174. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  175. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  176. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  177. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  178. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  179. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  180. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  181. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  182. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  183. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  184. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  185. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  186. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  187. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  188. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  189. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  190. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  191. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  192. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  193. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  194. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  195. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  196. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  197. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  198. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  199. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  200. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  201. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  202. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  203. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  204. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  205. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  206. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  207. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  208. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  209. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  210. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  211. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  212. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  213. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  214. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  215. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  216. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  217. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  218. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  219. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  220. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  221. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  222. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  223. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  224. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  225. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  226. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  227. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  228. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  229. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  230. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  231. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  232. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  233. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  234. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  235. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  236. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  237. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  238. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  239. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  240. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  241. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  242. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  243. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  244. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  245. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  246. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  247. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  248. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  249. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  250. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  251. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/docs/roadmap/items/workspace-scope-install.md +0 -0
  252. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/examples/README.md +0 -0
  253. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/examples/adapt-demo.sh +0 -0
  254. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/examples/boost-run-prototype.sh +0 -0
  255. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/index.html +0 -0
  256. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/pyproject.toml +0 -0
  257. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/bench_cli.sh +0 -0
  258. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/ensure_eval_corpus.sh +0 -0
  259. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/eval_explain.py +0 -0
  260. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/scripts/eval_recommend.py +0 -0
  261. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/setup.cfg +0 -0
  262. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/style/README.md +0 -0
  263. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/style/boost.css +0 -0
  264. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/style/boost.js +0 -0
  265. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/style/demo.html +0 -0
  266. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/adapt.feature +0 -0
  267. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/browse.feature +0 -0
  268. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/count.feature +0 -0
  269. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/discover.feature +0 -0
  270. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/doctor.feature +0 -0
  271. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/environment.py +0 -0
  272. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/explain.feature +0 -0
  273. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/index.feature +0 -0
  274. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/install.feature +0 -0
  275. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/list.feature +0 -0
  276. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/mcp.feature +0 -0
  277. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/search.feature +0 -0
  278. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/cli_steps.py +0 -0
  279. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/mcp_steps.py +0 -0
  280. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/quality_steps.py +0 -0
  281. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/conftest.py +0 -0
  282. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/eval/baseline.json +0 -0
  283. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/eval/explain.jsonl +0 -0
  284. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/eval/golden.jsonl +0 -0
  285. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/eval/recommend.jsonl +0 -0
  286. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/eval/taps.txt +0 -0
  287. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_cli_hooks.py +0 -0
  288. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/functional/test_property_parsers.py +0 -0
  289. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/perf/test_benchmarks.py +0 -0
  290. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/smoke.sh +0 -0
  291. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_adapters.py +0 -0
  292. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_agents.py +0 -0
  293. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_ai.py +0 -0
  294. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_cliparse.py +0 -0
  295. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_dense_fallback.py +0 -0
  296. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_embed.py +0 -0
  297. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_errors_and_cli_table.py +0 -0
  298. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_frontmatter.py +0 -0
  299. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_imperative.py +0 -0
  300. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_injectscan.py +0 -0
  301. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_mcp.py +0 -0
  302. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_nethttp.py +0 -0
  303. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_output.py +0 -0
  304. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_confusions.py +0 -0
  305. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_injection.py +0 -0
  306. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_secrets.py +0 -0
  307. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_rules.py +0 -0
  308. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_secretscan.py +0 -0
  309. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_spin.py +0 -0
  310. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_stackprobe.py +0 -0
  311. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_staleness.py +0 -0
  312. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_typosquat.py +0 -0
  313. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_updatediff.py +0 -0
  314. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_version.py +0 -0
  315. {boost_skill_cli-1.0.151 → boost_skill_cli-1.0.153}/tests/unit/test_workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.151
3
+ Version: 1.0.153
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -12,6 +12,12 @@ while [ -h "$SOURCE" ]; do
12
12
  [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
13
13
  done
14
14
  ROOT="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
15
+ # Under Git Bash on Windows, $ROOT is an MSYS path (/d/a/boost/boost); native
16
+ # Windows Python can't resolve that, so convert to a mixed path (D:/a/...)
17
+ # before it's embedded in the -c script below. No-op on Linux/macOS.
18
+ if command -v cygpath >/dev/null 2>&1; then
19
+ ROOT="$(cygpath -m "$ROOT")"
20
+ fi
15
21
 
16
22
  find_python() {
17
23
  for py in python3 python; do
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.151'
22
- __version_tuple__ = version_tuple = (1, 0, 151)
21
+ __version__ = version = '1.0.153'
22
+ __version_tuple__ = version_tuple = (1, 0, 153)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -224,6 +224,7 @@ def _extract_globals(argv: List[str]) -> tuple[dict, List[str]]:
224
224
 
225
225
  def main(argv: List[str] | None = None) -> int:
226
226
  argv = list(sys.argv[1:] if argv is None else argv)
227
+ out.harden_console_encoding()
227
228
  # Belt-and-suspenders fork safety: a host may fork() into `boost mcp
228
229
  # --stdio` without the no_proxy env boost injects at registration, leaving
229
230
  # the process one default getproxies() away from a macOS _scproxy SIGABRT.
@@ -192,7 +192,7 @@ def _copy_global_skills(modules) -> int:
192
192
  continue
193
193
  t = dest / d.name
194
194
  if t.exists():
195
- shutil.rmtree(t)
195
+ util.rmtree(t)
196
196
  shutil.copytree(d, t)
197
197
  n += 1
198
198
  return n
@@ -278,7 +278,7 @@ def _disable(scope) -> int:
278
278
  for d in sorted(_skills_dir(scope).glob("bmad-*")):
279
279
  target = qdir / d.name
280
280
  if target.exists():
281
- shutil.rmtree(target)
281
+ util.rmtree(target)
282
282
  shutil.move(str(d), str(target))
283
283
  moved += 1
284
284
  _set_scope_state(scope, startup=False, disabled=True)
@@ -298,7 +298,7 @@ def _enable(scope) -> int:
298
298
  for d in sorted(qdir.glob("bmad-*")):
299
299
  target = dest / d.name
300
300
  if target.exists():
301
- shutil.rmtree(target)
301
+ util.rmtree(target)
302
302
  shutil.move(str(d), str(target))
303
303
  restored += 1
304
304
  _set_scope_state(scope, disabled=False)
@@ -366,7 +366,7 @@ def _state_read() -> dict:
366
366
  if not p.exists():
367
367
  return {"global": {}, "projects": {}}
368
368
  try:
369
- d = json.loads(p.read_text())
369
+ d = json.loads(p.read_text(encoding="utf-8"))
370
370
  except (json.JSONDecodeError, OSError):
371
371
  return {"global": {}, "projects": {}}
372
372
  d.setdefault("global", {})
@@ -376,7 +376,7 @@ def _state_read() -> dict:
376
376
 
377
377
  def _state_write(d: dict) -> None:
378
378
  paths.ensure_dirs()
379
- _state_path().write_text(json.dumps(d, indent=2) + "\n")
379
+ _state_path().write_text(json.dumps(d, indent=2) + "\n", encoding="utf-8")
380
380
 
381
381
 
382
382
  def _proj_key() -> str:
@@ -152,7 +152,7 @@ def cmd_clean(argv) -> int:
152
152
  if pth.is_symlink() or pth.is_file():
153
153
  pth.unlink()
154
154
  elif pth.is_dir():
155
- shutil.rmtree(pth)
155
+ util.rmtree(pth)
156
156
  except OSError as e:
157
157
  out.warn("could not remove %s: %s" % (_tilde(pth), e))
158
158
  continue
@@ -224,7 +224,7 @@ def cmd_create(argv) -> int:
224
224
  title = name.replace("-", " ").title()
225
225
  target.mkdir(parents=True, exist_ok=True)
226
226
  skill_md.write_text(frontmatter.dump(meta) + "\n\n"
227
- + _CREATE_BODY % {"title": title})
227
+ + _CREATE_BODY % {"title": title}, encoding="utf-8")
228
228
  journal.log("create", name, path=str(target))
229
229
  out.ok("created %s" % _tilde(skill_md))
230
230
  if args.install:
@@ -426,7 +426,7 @@ def cmd_onboard(argv) -> int:
426
426
  for rel, content in files:
427
427
  fp = repo / rel
428
428
  fp.parent.mkdir(parents=True, exist_ok=True)
429
- fp.write_text(content)
429
+ fp.write_text(content, encoding="utf-8")
430
430
  out.ok("created %s" % _tilde(fp))
431
431
  journal.log("onboard", _tilde(repo), pr=args.pr or None)
432
432
 
@@ -599,7 +599,7 @@ def cmd_schedule(argv) -> int:
599
599
  if plist.exists():
600
600
  present = True
601
601
  m = re.search(r"<key>StartInterval</key>\s*<integer>(\d+)</integer>",
602
- plist.read_text())
602
+ plist.read_text(encoding="utf-8"))
603
603
  if m:
604
604
  secs = int(m.group(1))
605
605
  interval = _interval_label(secs)
@@ -641,7 +641,7 @@ def cmd_schedule(argv) -> int:
641
641
  if darwin:
642
642
  plist = _plist_path()
643
643
  plist.parent.mkdir(parents=True, exist_ok=True)
644
- plist.write_text(_plist_body(shim, seconds))
644
+ plist.write_text(_plist_body(shim, seconds), encoding="utf-8")
645
645
  out.ok("wrote %s" % _tilde(plist))
646
646
  try:
647
647
  subprocess.run(["launchctl", "unload", str(plist)],
@@ -252,7 +252,7 @@ def cmd_index(argv):
252
252
  paths.ensure_dirs()
253
253
  _discovery_path().write_text(json.dumps(
254
254
  {"generated": util.now_iso(), "github_total": total, "items": items},
255
- indent=1))
255
+ indent=1), encoding="utf-8")
256
256
  repos = len({it["repo"] for it in items})
257
257
  out.ok("indexed %d skill files across %d repos (GitHub reports %d total)"
258
258
  % (len(items), repos, total))
@@ -325,7 +325,7 @@ def cmd_discover(argv):
325
325
  "gh auth login`), then run `boost index`")
326
326
  return 0
327
327
  try:
328
- data = json.loads(dpath.read_text())
328
+ data = json.loads(dpath.read_text(encoding="utf-8"))
329
329
  except (json.JSONDecodeError, OSError):
330
330
  raise BoostError("the discovery index is corrupt",
331
331
  hint="rebuild it with `boost index`")
@@ -979,7 +979,7 @@ def cmd_count(argv):
979
979
  dpath = _discovery_path()
980
980
  if dpath.exists():
981
981
  try:
982
- discovery = len(json.loads(dpath.read_text()).get("items") or [])
982
+ discovery = len(json.loads(dpath.read_text(encoding="utf-8")).get("items") or [])
983
983
  except (json.JSONDecodeError, OSError):
984
984
  discovery = None
985
985
  if args.as_json:
@@ -60,14 +60,14 @@ def _load_state(fname: str, default: dict) -> dict:
60
60
  if not p.exists():
61
61
  return json.loads(json.dumps(default))
62
62
  try:
63
- return json.loads(p.read_text())
63
+ return json.loads(p.read_text(encoding="utf-8"))
64
64
  except (json.JSONDecodeError, OSError):
65
65
  return json.loads(json.dumps(default))
66
66
 
67
67
 
68
68
  def _save_state(fname: str, data: dict) -> None:
69
69
  paths.ensure_dirs()
70
- (paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n")
70
+ (paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
71
71
 
72
72
 
73
73
  def _install_generated(name: str, text: str) -> None:
@@ -75,7 +75,7 @@ def _install_generated(name: str, text: str) -> None:
75
75
  with tempfile.TemporaryDirectory(prefix="boost-gen-") as td:
76
76
  src = Path(td) / name
77
77
  src.mkdir()
78
- (src / "SKILL.md").write_text(text)
78
+ (src / "SKILL.md").write_text(text, encoding="utf-8")
79
79
  res = store.install_from_path(src, name=name, tap_label="local")
80
80
  out.ok("installed %s → %s" % (name, _tilde(res.dest)))
81
81
  if res.linked:
@@ -88,7 +88,7 @@ def _write_generated(dest: Path, text: str) -> bool:
88
88
  out.info("aborted — %s left untouched" % _tilde(dest))
89
89
  return False
90
90
  dest.parent.mkdir(parents=True, exist_ok=True)
91
- dest.write_text(text)
91
+ dest.write_text(text, encoding="utf-8")
92
92
  out.ok("wrote %s" % _tilde(dest))
93
93
  return True
94
94
 
@@ -649,7 +649,7 @@ def cmd_evolve(argv: List[str]) -> int:
649
649
  if not args.apply:
650
650
  out.info(out.role("re-run with --apply to write these changes", "muted"))
651
651
  return 0
652
- skill_md.write_text(new)
652
+ skill_md.write_text(new, encoding="utf-8")
653
653
  entry["sha256"] = util.sha256_dir(store.skill_store_dir(args.name))
654
654
  entry["updated_at"] = util.now_iso()
655
655
  entry["version"] = new_ver
@@ -22,14 +22,7 @@ from ..core import output as out
22
22
  from ..errors import BoostError
23
23
 
24
24
 
25
- def _tilde(p) -> str:
26
- """Contract $HOME to ~ for display (also matches the resolved home,
27
- so /private/var vs /var symlinks on macOS still contract)."""
28
- s = str(p)
29
- for h in (str(paths.home()), str(paths.home().resolve())):
30
- if s == h or s.startswith(h + os.sep):
31
- return "~" + s[len(h):]
32
- return s
25
+ _tilde = paths.tilde
33
26
 
34
27
 
35
28
  def _plural(n: int, word: str) -> str:
@@ -338,7 +331,7 @@ def cmd_sync(argv: List[str]) -> int:
338
331
  for name in orphans:
339
332
  target = store.skill_store_dir(name)
340
333
  if target.is_dir():
341
- shutil.rmtree(target)
334
+ util.rmtree(target)
342
335
  journal.log("prune", name)
343
336
  pruned.append(name)
344
337
  left = [n for n in orphans if n not in pruned]
@@ -572,7 +565,7 @@ def _bundle_dump(file: Optional[str]) -> int:
572
565
  return 0
573
566
  dest = paths.expand(file)
574
567
  try:
575
- dest.write_text(text)
568
+ dest.write_text(text, encoding="utf-8")
576
569
  except OSError as e:
577
570
  raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
578
571
  hint="check the path exists and is writable")
@@ -596,7 +589,7 @@ def _bundle_install(file: Optional[str]) -> int:
596
589
  hint="point at the Boostfile itself, "
597
590
  "or use `boost import` for skill directories")
598
591
  try:
599
- text, label = path.read_text(), str(path)
592
+ text, label = path.read_text(encoding="utf-8"), str(path)
600
593
  except OSError as e:
601
594
  raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e))
602
595
  taps_added = installed_n = present = failed = 0
@@ -880,7 +873,7 @@ def _snapshot_save(label: Optional[str]) -> int:
880
873
  manifest = {"id": snap_id, "label": label or "", "created": util.now_iso(),
881
874
  "skills": skill_count}
882
875
  (paths.snapshots_dir() / (snap_id + ".json")).write_text(
883
- json.dumps(manifest, indent=2) + "\n")
876
+ json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
884
877
  journal.log("snapshot", snap_id, label=label)
885
878
  out.ok("saved %s (%s, %s)" % (snap_id, _plural(skill_count, "skill"),
886
879
  util.human_size(tar_path.stat().st_size)))
@@ -900,7 +893,7 @@ def _snapshot_list(as_json: bool) -> int:
900
893
  meta = {}
901
894
  if side.exists():
902
895
  try:
903
- meta = json.loads(side.read_text())
896
+ meta = json.loads(side.read_text(encoding="utf-8"))
904
897
  except (json.JSONDecodeError, OSError):
905
898
  meta = {}
906
899
  snaps.append({"id": snap_id, "created": meta.get("created", ""),
@@ -950,7 +943,7 @@ def _snapshot_restore(snap_id: str) -> int:
950
943
  "see `boost snapshot list` for other snapshots")
951
944
  for child in root.iterdir():
952
945
  if child.is_dir() and not child.is_symlink():
953
- shutil.rmtree(child)
946
+ util.rmtree(child)
954
947
  else:
955
948
  child.unlink()
956
949
  try:
@@ -1006,7 +999,7 @@ def cmd_export(argv: List[str]) -> int:
1006
999
  for name in chosen:
1007
1000
  sdir = store.skill_store_dir(name)
1008
1001
  for f in sorted(p for p in sdir.rglob("*") if p.is_file()):
1009
- zf.write(str(f), arcname="%s/%s" % (name, f.relative_to(sdir)))
1002
+ zf.write(str(f), arcname="%s/%s" % (name, f.relative_to(sdir).as_posix()))
1010
1003
  else:
1011
1004
  with tarfile.open(str(dest), "w:gz") as tf:
1012
1005
  data = manifest.encode()
@@ -269,7 +269,7 @@ def cmd_doctor(argv):
269
269
  lp = paths.lockfile_path()
270
270
  if lp.exists():
271
271
  try:
272
- raw = json.loads(lp.read_text())
272
+ raw = json.loads(lp.read_text(encoding="utf-8"))
273
273
  if raw.get("version") != lockfile.SCHEMA_VERSION:
274
274
  bad("lock file schema is v%s, expected v%d"
275
275
  % (raw.get("version"), lockfile.SCHEMA_VERSION))
@@ -499,7 +499,7 @@ def cmd_audit(argv):
499
499
  text = f.read_text(encoding="utf-8", errors="replace")
500
500
  except OSError:
501
501
  continue
502
- rel = str(f.relative_to(sdir))
502
+ rel = f.relative_to(sdir).as_posix()
503
503
  for pat, severity, label in _AUDIT_PATTERNS:
504
504
  for m in pat.finditer(text):
505
505
  line_no = text.count("\n", 0, m.start()) + 1
@@ -157,7 +157,7 @@ def _tap_updated(tap: "registry.Tap") -> str:
157
157
  except BoostError:
158
158
  pass
159
159
  try:
160
- data = json.loads(tap.cache_file.read_text())
160
+ data = json.loads(tap.cache_file.read_text(encoding="utf-8"))
161
161
  return util.rel_time(data.get("generated", ""))
162
162
  except (OSError, ValueError):
163
163
  return "?"
@@ -40,14 +40,14 @@ def _load_cohorts() -> dict:
40
40
  if not p.exists():
41
41
  return {}
42
42
  try:
43
- return json.loads(p.read_text())
43
+ return json.loads(p.read_text(encoding="utf-8"))
44
44
  except (json.JSONDecodeError, OSError):
45
45
  return {}
46
46
 
47
47
 
48
48
  def _save_cohorts(cohorts: dict) -> None:
49
49
  paths.ensure_dirs()
50
- _cohorts_path().write_text(json.dumps(cohorts, indent=2) + "\n")
50
+ _cohorts_path().write_text(json.dumps(cohorts, indent=2) + "\n", encoding="utf-8")
51
51
 
52
52
 
53
53
  def _is_member(user: str, cohort_name: str, percent: int) -> bool:
@@ -181,7 +181,7 @@ def _load_profile(name: str) -> dict:
181
181
  raise BoostError("no profile named %s" % name,
182
182
  hint="list profiles with `boost profile list`")
183
183
  try:
184
- return json.loads(p.read_text())
184
+ return json.loads(p.read_text(encoding="utf-8"))
185
185
  except (json.JSONDecodeError, OSError) as e:
186
186
  raise BoostError("profile %s is unreadable: %s" % (name, e))
187
187
 
@@ -217,7 +217,7 @@ def cmd_profile(argv) -> int:
217
217
  profiles = []
218
218
  for f in sorted(paths.profiles_dir().glob("*.json")):
219
219
  try:
220
- data = json.loads(f.read_text())
220
+ data = json.loads(f.read_text(encoding="utf-8"))
221
221
  except (json.JSONDecodeError, OSError):
222
222
  continue
223
223
  profiles.append({"name": data.get("name", f.stem),
@@ -242,7 +242,7 @@ def cmd_profile(argv) -> int:
242
242
  "version": e.get("version", "0.0.0")}
243
243
  for n, e in installed.items()}}
244
244
  paths.ensure_dirs()
245
- _profile_path(args.name).write_text(json.dumps(profile, indent=2) + "\n")
245
+ _profile_path(args.name).write_text(json.dumps(profile, indent=2) + "\n", encoding="utf-8")
246
246
  journal.log("profile", args.name, op="save", skills=len(installed))
247
247
  out.ok("saved profile %s (%d skills)" % (args.name, len(installed)))
248
248
  return 0
@@ -390,7 +390,7 @@ def cmd_protocol(argv) -> int:
390
390
  script = _handler_script()
391
391
  script.write_text("#!/usr/bin/env bash\n"
392
392
  "# boost:// URL handler — invoked with the URL as $1\n"
393
- 'exec "%s" protocol open "$1"\n' % shim)
393
+ 'exec "%s" protocol open "$1"\n' % shim, encoding="utf-8")
394
394
  script.chmod(script.stat().st_mode | stat.S_IEXEC)
395
395
  out.ok("wrote handler script %s" % _tilde(script))
396
396
  if system == "Darwin":
@@ -404,7 +404,7 @@ def cmd_protocol(argv) -> int:
404
404
  desktop.parent.mkdir(parents=True, exist_ok=True)
405
405
  desktop.write_text("[Desktop Entry]\nType=Application\nName=boost protocol handler\n"
406
406
  "Exec=%s %%u\nMimeType=x-scheme-handler/boost;\nNoDisplay=true\n"
407
- % script)
407
+ % script, encoding="utf-8")
408
408
  out.ok("wrote %s" % _tilde(desktop))
409
409
  if shutil.which("xdg-mime"):
410
410
  import subprocess
@@ -74,8 +74,8 @@ def _make_entry(root: Path, defining_file: Path, kind: str, default_name: str,
74
74
  "tap": tap_name,
75
75
  "curated": curated,
76
76
  "kind": kind,
77
- "rel_dir": str(item_dir.relative_to(root)) if item_dir != root else ".",
78
- "skill_md": str(defining_file.relative_to(root)),
77
+ "rel_dir": item_dir.relative_to(root).as_posix() if item_dir != root else ".",
78
+ "skill_md": defining_file.relative_to(root).as_posix(),
79
79
  "meta": meta,
80
80
  # Lowercased substring-search blob, flattened once at index time so
81
81
  # search() never re-walks the frontmatter per query (see _search_blob).
@@ -166,7 +166,7 @@ def rebuild_tap(tap: "registry.Tap") -> List[dict]:
166
166
  "generated": util.now_iso(),
167
167
  "commit": gitutil.head_commit(tap.path),
168
168
  "skills": entries,
169
- }, indent=1))
169
+ }, indent=1), encoding="utf-8")
170
170
  # Drop the mtime-keyed cache so a rebuild is visible to an immediately
171
171
  # following load even when the filesystem mtime granularity is coarse.
172
172
  _ENTRY_CACHE.pop(str(tap.cache_file), None)
@@ -197,7 +197,7 @@ def _cached_tap(tap: "registry.Tap") -> Optional[List[dict]]:
197
197
  if cached is not None and cached[0] == stamp:
198
198
  return cached[1]
199
199
  try:
200
- skills = json.loads(p.read_text()).get("skills", [])
200
+ skills = json.loads(p.read_text(encoding="utf-8")).get("skills", [])
201
201
  except (json.JSONDecodeError, OSError):
202
202
  _ENTRY_CACHE.pop(key, None)
203
203
  return None
@@ -59,7 +59,7 @@ def load(scope: str, project_dir: Optional[Path] = None) -> dict:
59
59
  if not p.exists():
60
60
  return {}
61
61
  try:
62
- data = json.loads(p.read_text())
62
+ data = json.loads(p.read_text(encoding="utf-8"))
63
63
  except (json.JSONDecodeError, OSError):
64
64
  return {}
65
65
  return data if isinstance(data, dict) else {}
@@ -78,9 +78,9 @@ def save(scope: str, data: dict, project_dir: Optional[Path] = None) -> None:
78
78
  while dest.exists():
79
79
  dest = hist / ("%s-%s-%d.json" % (scope, stamp, n))
80
80
  n += 1
81
- dest.write_text(p.read_text())
81
+ dest.write_text(p.read_text(encoding="utf-8"), encoding="utf-8")
82
82
  _prune_history()
83
- p.write_text(json.dumps(data, indent=2) + "\n")
83
+ p.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
84
84
 
85
85
 
86
86
  def _prune_history() -> None:
@@ -61,7 +61,7 @@ REGISTRY_CATALOG = paths.package_root() / "data" / "registries.json"
61
61
  def load_registry_catalog() -> list:
62
62
  """The bundled curated registries, or [] if the data file is missing."""
63
63
  try:
64
- data = json.loads(REGISTRY_CATALOG.read_text())
64
+ data = json.loads(REGISTRY_CATALOG.read_text(encoding="utf-8"))
65
65
  except (OSError, json.JSONDecodeError):
66
66
  return []
67
67
  return data.get("registries", [])
@@ -105,7 +105,7 @@ def _read() -> dict:
105
105
  if not p.exists():
106
106
  return deepcopy(DEFAULTS)
107
107
  try:
108
- user = json.loads(p.read_text())
108
+ user = json.loads(p.read_text(encoding="utf-8"))
109
109
  except (json.JSONDecodeError, OSError):
110
110
  return deepcopy(DEFAULTS)
111
111
  return _merge(DEFAULTS, user)
@@ -230,10 +230,18 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
230
230
  return None
231
231
  try:
232
232
  pool = max(k * _POOL, 200)
233
- knn = con.execute(
234
- "SELECT rowid, distance FROM vec_chunks "
235
- "WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
236
- (mod.serialize_float32(qv[0]), pool)).fetchall()
233
+ try:
234
+ knn = con.execute(
235
+ "SELECT rowid, distance FROM vec_chunks "
236
+ "WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
237
+ (mod.serialize_float32(qv[0]), pool)).fetchall()
238
+ except sqlite3.OperationalError:
239
+ # Some sqlite3/sqlite-vec combinations (observed on Windows)
240
+ # reject this exact query shape ("a LIMIT or 'k = ?' constraint
241
+ # is required") despite the bound LIMIT above — dense retrieval
242
+ # is genuinely unusable here, so fall back to BM25 rather than
243
+ # crash the search command.
244
+ return None
237
245
  if not knn:
238
246
  return []
239
247
  by_id = {r[0]: (r[1], r[2], r[3], r[4]) for r in con.execute(
@@ -31,7 +31,7 @@ def events(n: Optional[int] = None, action: Optional[str] = None,
31
31
  if not p.exists():
32
32
  return []
33
33
  out = []
34
- for line in p.read_text().splitlines():
34
+ for line in p.read_text(encoding="utf-8").splitlines():
35
35
  try:
36
36
  e = json.loads(line)
37
37
  except json.JSONDecodeError:
@@ -55,7 +55,7 @@ def rotation_healthy() -> bool:
55
55
  def _maybe_rotate() -> None:
56
56
  p = paths.pulse_path()
57
57
  try:
58
- lines = p.read_text().splitlines()
58
+ lines = p.read_text(encoding="utf-8").splitlines()
59
59
  except OSError:
60
60
  return
61
61
  if len(lines) > ROTATE_AT:
@@ -29,7 +29,7 @@ def read() -> dict:
29
29
  if not p.exists():
30
30
  return _skeleton() # empty: never installed anything yet
31
31
  try:
32
- lock = json.loads(p.read_text())
32
+ lock = json.loads(p.read_text(encoding="utf-8"))
33
33
  except (json.JSONDecodeError, OSError):
34
34
  # Corrupt (present but unparseable) is NOT the same as empty: returning
35
35
  # a bare skeleton here would let the next write() overwrite the only
@@ -169,7 +169,7 @@ def history_list() -> List[dict]:
169
169
  out = []
170
170
  for p in _history_files():
171
171
  try:
172
- data = json.loads(p.read_text())
172
+ data = json.loads(p.read_text(encoding="utf-8"))
173
173
  except (json.JSONDecodeError, OSError):
174
174
  continue
175
175
  out.append({
@@ -187,4 +187,4 @@ def history_read(hist_id: str) -> dict:
187
187
  from ..errors import BoostError
188
188
  raise BoostError("no lock history entry %s" % hist_id,
189
189
  hint="list entries with `boost replay`")
190
- return json.loads(p.read_text())
190
+ return json.loads(p.read_text(encoding="utf-8"))
@@ -25,6 +25,23 @@ MAGENTA = "\033[35m"
25
25
  CYAN = "\033[36m"
26
26
 
27
27
 
28
+ def harden_console_encoding() -> None:
29
+ """Reconfigure stdout/stderr to UTF-8.
30
+
31
+ boost's own output uses non-ASCII (checkmarks, em dashes, arrows); a
32
+ console whose codepage isn't UTF-8 — chiefly Windows' legacy codepages —
33
+ raises UnicodeEncodeError on that output otherwise. reconfigure() is
34
+ missing on some wrapped/piped streams (e.g. under certain test harnesses),
35
+ so skip a stream that doesn't have it rather than fail startup over it.
36
+ """
37
+ for stream in (sys.stdout, sys.stderr):
38
+ if hasattr(stream, "reconfigure"):
39
+ try:
40
+ stream.reconfigure(encoding="utf-8", errors="replace")
41
+ except (OSError, ValueError):
42
+ pass
43
+
44
+
28
45
  def use_color(stream=None) -> bool:
29
46
  # BOOST_COLOR is boost's own explicit override and wins over everything
30
47
  # else (most-specific-wins): always/never force it on/off regardless of
@@ -39,14 +39,18 @@ def tilde(p) -> str:
39
39
  directly beneath it (``home + os.sep``). A bare ``startswith(home)`` would
40
40
  wrongly turn a sibling like ``/Users/bob-backup`` into ``~-backup``. Both
41
41
  the raw and resolved forms of home() are tried so symlinked homes contract.
42
+
43
+ Always returns ``/``-separated output (even for a path outside $HOME) so
44
+ boost's display text is stable across platforms — Windows accepts forward
45
+ slashes natively, so nothing is lost for a Windows reader either.
42
46
  """
43
47
  s = str(p)
44
48
  for h in (str(home()), str(home().resolve())):
45
49
  if s == h:
46
50
  return "~"
47
51
  if s.startswith(h + os.sep):
48
- return "~" + s[len(h):]
49
- return s
52
+ return ("~" + s[len(h):]).replace(os.sep, "/")
53
+ return s.replace(os.sep, "/")
50
54
 
51
55
 
52
56
  def boost_home() -> Path:
@@ -27,7 +27,7 @@ def load() -> dict:
27
27
  base = dict(DEFAULTS)
28
28
  if p.exists():
29
29
  try:
30
- base.update(json.loads(p.read_text()))
30
+ base.update(json.loads(p.read_text(encoding="utf-8")))
31
31
  except (json.JSONDecodeError, OSError):
32
32
  pass
33
33
  return base
@@ -36,7 +36,7 @@ def load() -> dict:
36
36
  def save(pol: dict) -> None:
37
37
  paths.ensure_dirs()
38
38
  known = {k: pol.get(k, DEFAULTS[k]) for k in DEFAULTS}
39
- paths.policy_path().write_text(json.dumps(known, indent=2) + "\n")
39
+ paths.policy_path().write_text(json.dumps(known, indent=2) + "\n", encoding="utf-8")
40
40
 
41
41
 
42
42
  def check_install(entry: dict, installed_count: int) -> List[str]:
@@ -122,7 +122,7 @@ def _tap_commits() -> Dict[str, str]:
122
122
  for t in registry.list_taps():
123
123
  commit = ""
124
124
  try:
125
- data = json.loads(t.cache_file.read_text())
125
+ data = json.loads(t.cache_file.read_text(encoding="utf-8"))
126
126
  commit = str(data.get("commit") or "")
127
127
  except (OSError, json.JSONDecodeError):
128
128
  pass
@@ -288,7 +288,7 @@ def _load_raw() -> Optional[dict]:
288
288
  if isinstance(cached, tuple) and cached[0] == mtime:
289
289
  return cached[1] # type: ignore[return-value]
290
290
  try:
291
- data = json.loads(p.read_text())
291
+ data = json.loads(p.read_text(encoding="utf-8"))
292
292
  except (OSError, json.JSONDecodeError):
293
293
  return None
294
294
  if not isinstance(data, dict) or data.get("version") != INDEX_VERSION:
@@ -2,13 +2,12 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import difflib
5
- import shutil
6
5
  from dataclasses import dataclass
7
6
  from pathlib import Path
8
7
  from typing import List, Optional
9
8
 
10
9
  from ..errors import BoostError
11
- from . import config, gitutil, paths
10
+ from . import config, gitutil, paths, util
12
11
 
13
12
 
14
13
  @dataclass
@@ -40,7 +39,10 @@ def parse_spec(spec: str):
40
39
  Accepts: owner/repo | full git URL | existing local path.
41
40
  """
42
41
  spec = spec.strip().rstrip("/")
43
- p = Path(spec).expanduser()
42
+ # paths.expand(), not Path.expanduser(): expanduser() consults the OS's
43
+ # own home-dir lookup (USERPROFILE on Windows), which ignores the $HOME
44
+ # override the whole test suite (and BOOST_HOME sandboxing) relies on.
45
+ p = paths.expand(spec)
44
46
  if p.exists() and p.is_dir():
45
47
  return (p.resolve().name, str(p.resolve()))
46
48
  if spec.startswith(("http://", "https://", "git@", "ssh://")):
@@ -78,7 +80,7 @@ def add(spec: str, curated: bool = False) -> Tap:
78
80
  tap = Tap(name=name, url=url, curated=curated)
79
81
  paths.ensure_dirs()
80
82
  if tap.path.exists():
81
- shutil.rmtree(tap.path)
83
+ util.rmtree(tap.path)
82
84
  gitutil.clone_shallow(url, tap.path)
83
85
  cfg = config.load()
84
86
  cfg.setdefault("taps", []).append(
@@ -93,7 +95,7 @@ def remove(name: str) -> Tap:
93
95
  cfg["taps"] = [t for t in cfg.get("taps", []) if t["name"] != tap.name]
94
96
  config.save(cfg)
95
97
  if tap.path.exists():
96
- shutil.rmtree(tap.path)
98
+ util.rmtree(tap.path)
97
99
  if tap.cache_file.exists():
98
100
  tap.cache_file.unlink()
99
101
  return tap