boost-skill-cli 1.0.152__tar.gz → 1.0.154__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.152 → boost_skill_cli-1.0.154}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost +6 -0
  3. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/_version.py +2 -2
  4. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/cli.py +6 -6
  5. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/cliparse.py +1 -1
  6. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/bmad.py +6 -6
  7. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/configuration.py +25 -19
  8. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/discovery.py +11 -14
  9. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/info.py +9 -7
  10. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/intelligence.py +9 -11
  11. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/pkg.py +15 -22
  12. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/quality.py +14 -14
  13. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/taps.py +1 -1
  14. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/team.py +8 -8
  15. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/catalog.py +4 -4
  16. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/claude_settings.py +5 -6
  17. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/config.py +2 -2
  18. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/dense.py +12 -4
  19. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/frontmatter.py +1 -2
  20. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/gitutil.py +2 -2
  21. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/injectscan.py +6 -4
  22. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/journal.py +2 -2
  23. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/lockfile.py +3 -3
  24. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/logs.py +4 -9
  25. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/mcp.py +1 -1
  26. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/output.py +17 -1
  27. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/paths.py +6 -2
  28. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/policy.py +4 -5
  29. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/rag.py +4 -4
  30. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/registry.py +7 -5
  31. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/serve.py +11 -7
  32. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/staleness.py +2 -3
  33. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/store.py +2 -2
  34. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/updatediff.py +2 -5
  35. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/util.py +26 -4
  36. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  37. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +3 -3
  38. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/windows-in-the-ci-matrix.md +3 -3
  39. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap.html +2 -2
  40. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/pyproject.toml +17 -4
  41. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/build_registries.py +2 -2
  42. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/build_roadmap.py +5 -5
  43. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/eval_retrieval.py +2 -2
  44. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/mutation_gate.py +1 -1
  45. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/steps/discovery_steps.py +2 -2
  46. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_adapt.py +1 -1
  47. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_bmad.py +2 -2
  48. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_configuration.py +17 -18
  49. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_discovery.py +51 -26
  50. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_info.py +18 -10
  51. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_intelligence.py +24 -25
  52. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_pkg.py +23 -23
  53. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_quality.py +10 -10
  54. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_taps.py +10 -10
  55. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_team.py +12 -9
  56. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_everyday_loop.py +2 -2
  57. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_property_parsers.py +3 -3
  58. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/make_fixture.py +4 -2
  59. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_adapters.py +2 -1
  60. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_catalog.py +39 -34
  61. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_claude_settings.py +3 -3
  62. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_config.py +10 -10
  63. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_dense.py +38 -3
  64. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_dense_fallback.py +3 -2
  65. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_docs_pages_linked.py +1 -1
  66. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_gitutil.py +3 -3
  67. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_journal.py +9 -9
  68. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_lockfile.py +14 -14
  69. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_logs.py +11 -11
  70. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_mcp.py +4 -4
  71. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_mutation_hardening.py +3 -3
  72. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_output.py +2 -2
  73. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_paths.py +6 -4
  74. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_policy.py +4 -4
  75. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_rag.py +18 -18
  76. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_registries_fresh.py +1 -1
  77. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_registry.py +6 -7
  78. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_roadmap_fresh.py +2 -2
  79. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_serve.py +55 -4
  80. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_store.py +29 -28
  81. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_token_parity.py +1 -1
  82. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_typosquat.py +3 -1
  83. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_util.py +10 -10
  84. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/.gitattributes +0 -0
  85. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/.gitignore +0 -0
  86. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/CLAUDE.md +0 -0
  87. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/CONTRIBUTING.md +0 -0
  88. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/LICENSE +0 -0
  89. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/MANIFEST.in +0 -0
  90. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/Makefile +0 -0
  91. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/README.md +0 -0
  92. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/SECURITY.md +0 -0
  93. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/__init__.py +0 -0
  94. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/__main__.py +0 -0
  95. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/__init__.py +0 -0
  96. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/commands/hooks.py +0 -0
  97. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/__init__.py +0 -0
  98. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/adapters.py +0 -0
  99. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/agents.py +0 -0
  100. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/ai.py +0 -0
  101. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/embed.py +0 -0
  102. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/imperative.py +0 -0
  103. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/nethttp.py +0 -0
  104. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/rules.py +0 -0
  105. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/secretscan.py +0 -0
  106. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/stackprobe.py +0 -0
  107. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/typosquat.py +0 -0
  108. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/core/workflows.py +0 -0
  109. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/data/registries.json +0 -0
  110. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/errors.py +0 -0
  111. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_cli/spin.py +0 -0
  112. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
  113. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  114. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  115. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/requires.txt +0 -0
  116. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/boost_skill_cli.egg-info/top_level.txt +0 -0
  117. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/.claude/settings.local.json +0 -0
  118. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/DEBUGGING.md +0 -0
  119. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/adapters.html +0 -0
  120. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/demo.gif +0 -0
  121. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/demo.tape +0 -0
  122. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/design-roadmap.html +0 -0
  123. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/eval.html +0 -0
  124. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/index.html +0 -0
  125. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/mcp-hub.html +0 -0
  126. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/rag-architecture.md +0 -0
  127. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D01.md +0 -0
  128. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D02.md +0 -0
  129. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D03.md +0 -0
  130. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D04.md +0 -0
  131. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D05.md +0 -0
  132. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D06.md +0 -0
  133. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D07.md +0 -0
  134. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D08.md +0 -0
  135. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D09.md +0 -0
  136. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D10.md +0 -0
  137. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D11.md +0 -0
  138. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D12.md +0 -0
  139. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D13.md +0 -0
  140. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D14.md +0 -0
  141. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D15.md +0 -0
  142. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D16.md +0 -0
  143. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D17.md +0 -0
  144. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D18.md +0 -0
  145. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D19.md +0 -0
  146. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D20.md +0 -0
  147. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D21.md +0 -0
  148. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D22.md +0 -0
  149. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D23.md +0 -0
  150. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D24.md +0 -0
  151. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D25.md +0 -0
  152. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/BOOST-D26.md +0 -0
  153. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  154. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  155. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  156. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  157. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  158. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  159. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  160. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  161. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  162. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  163. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  164. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  165. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  166. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  167. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  168. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  169. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  170. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  171. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  172. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  173. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  174. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  175. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  176. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  177. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  178. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  179. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  180. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  181. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  182. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  183. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  184. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  185. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  186. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  187. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  188. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  189. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  190. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  191. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  192. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  193. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  194. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  195. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  196. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  197. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  198. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  199. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  200. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  201. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  202. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  203. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  204. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  205. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  206. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  207. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  208. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  209. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  210. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  211. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  212. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  213. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  214. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  215. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  216. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  217. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  218. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  219. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  220. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  221. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  222. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  223. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  224. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  225. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  226. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  227. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  228. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  229. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  230. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  231. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  232. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  233. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  234. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  235. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  236. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  237. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  238. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  239. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  240. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  241. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  242. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  243. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  244. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  245. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  246. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  247. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  248. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  249. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  250. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  251. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  252. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  253. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  254. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  255. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  256. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  257. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  258. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/docs/roadmap/items/workspace-scope-install.md +0 -0
  259. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/examples/README.md +0 -0
  260. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/examples/adapt-demo.sh +0 -0
  261. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/examples/boost-run-prototype.sh +0 -0
  262. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/index.html +0 -0
  263. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/bench_cli.sh +0 -0
  264. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/ensure_eval_corpus.sh +0 -0
  265. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/eval_explain.py +0 -0
  266. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/scripts/eval_recommend.py +0 -0
  267. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/setup.cfg +0 -0
  268. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/style/README.md +0 -0
  269. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/style/boost.css +0 -0
  270. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/style/boost.js +0 -0
  271. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/style/demo.html +0 -0
  272. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/adapt.feature +0 -0
  273. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/browse.feature +0 -0
  274. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/count.feature +0 -0
  275. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/discover.feature +0 -0
  276. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/doctor.feature +0 -0
  277. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/environment.py +0 -0
  278. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/explain.feature +0 -0
  279. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/index.feature +0 -0
  280. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/install.feature +0 -0
  281. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/list.feature +0 -0
  282. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/mcp.feature +0 -0
  283. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/search.feature +0 -0
  284. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/steps/cli_steps.py +0 -0
  285. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/steps/mcp_steps.py +0 -0
  286. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/bdd/features/steps/quality_steps.py +0 -0
  287. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/conftest.py +0 -0
  288. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/eval/baseline.json +0 -0
  289. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/eval/explain.jsonl +0 -0
  290. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/eval/golden.jsonl +0 -0
  291. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/eval/recommend.jsonl +0 -0
  292. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/eval/taps.txt +0 -0
  293. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/functional/test_cli_hooks.py +0 -0
  294. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/perf/test_benchmarks.py +0 -0
  295. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/smoke.sh +0 -0
  296. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_agents.py +0 -0
  297. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_ai.py +0 -0
  298. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_cliparse.py +0 -0
  299. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_embed.py +0 -0
  300. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_errors_and_cli_table.py +0 -0
  301. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_frontmatter.py +0 -0
  302. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_imperative.py +0 -0
  303. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_injectscan.py +0 -0
  304. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_nethttp.py +0 -0
  305. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_pkg_confusions.py +0 -0
  306. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_pkg_injection.py +0 -0
  307. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_pkg_secrets.py +0 -0
  308. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_rules.py +0 -0
  309. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_secretscan.py +0 -0
  310. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_spin.py +0 -0
  311. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_stackprobe.py +0 -0
  312. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_staleness.py +0 -0
  313. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_updatediff.py +0 -0
  314. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/tests/unit/test_version.py +0 -0
  315. {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.154}/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.152
3
+ Version: 1.0.154
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.152'
22
- __version_tuple__ = version_tuple = (1, 0, 152)
21
+ __version__ = version = '1.0.154'
22
+ __version_tuple__ = version_tuple = (1, 0, 154)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -17,6 +17,7 @@ from .core import logs
17
17
  from .core import nethttp
18
18
  from .core import output as out
19
19
  from .errors import BoostError
20
+ import contextlib
20
21
 
21
22
  # Global flags handled before dispatch, stripped from the command's own argv.
22
23
  _GLOBAL_FLAGS = {
@@ -174,7 +175,7 @@ def print_command_help(name: str) -> int:
174
175
  meta = resolve(name)
175
176
  if not meta:
176
177
  return _unknown(name)
177
- _group, module, summary = meta
178
+ _group, _module, summary = meta
178
179
  print(out.c("boost %s" % name, out.BOLD) + " — " + summary)
179
180
  # delegate to the command's own --help for flags
180
181
  return _dispatch(name, ["--help"], soft=True)
@@ -196,7 +197,7 @@ def _unknown(name: str) -> int:
196
197
 
197
198
 
198
199
  def _dispatch(name: str, argv: List[str], soft: bool = False) -> int:
199
- group, module, _summary = _BY_NAME[name]
200
+ _group, module, _summary = _BY_NAME[name]
200
201
  mod = importlib.import_module("boost_cli.commands.%s" % module)
201
202
  func = getattr(mod, "cmd_%s" % name.replace("-", "_"), None)
202
203
  if func is None:
@@ -224,6 +225,7 @@ def _extract_globals(argv: List[str]) -> tuple[dict, List[str]]:
224
225
 
225
226
  def main(argv: List[str] | None = None) -> int:
226
227
  argv = list(sys.argv[1:] if argv is None else argv)
228
+ out.harden_console_encoding()
227
229
  # Belt-and-suspenders fork safety: a host may fork() into `boost mcp
228
230
  # --stdio` without the no_proxy env boost injects at registration, leaving
229
231
  # the process one default getproxies() away from a macOS _scproxy SIGABRT.
@@ -263,13 +265,11 @@ def main(argv: List[str] | None = None) -> int:
263
265
  print()
264
266
  return rc
265
267
  except BrokenPipeError:
266
- try:
268
+ with contextlib.suppress(Exception):
267
269
  sys.stdout.close()
268
- except Exception:
269
- pass
270
270
  rc = 0
271
271
  return rc
272
- except Exception as e: # noqa: BLE001 — top-level safety net
272
+ except Exception as e:
273
273
  report = logs.write_crash_report(e, [name, *rest])
274
274
  if logs.is_debug():
275
275
  raise
@@ -19,7 +19,7 @@ from .core import output as out
19
19
 
20
20
 
21
21
  class BoostArgumentParser(argparse.ArgumentParser):
22
- def error(self, message: str): # noqa: D401 - argparse hook
22
+ def error(self, message: str):
23
23
  """Print a branded error + dimmed usage, then exit 2 (argparse's code)."""
24
24
  out.err(message)
25
25
  sys.stderr.write(out.c(self.format_usage(), out.DIM))
@@ -119,7 +119,7 @@ def _run_installer(directory: Path, modules: str) -> "subprocess.CompletedProces
119
119
  proc = subprocess.run(cmd, capture_output=True, text=True, timeout=900)
120
120
  except (OSError, subprocess.TimeoutExpired) as e:
121
121
  raise BoostError("bmad install failed: %s" % e,
122
- hint="run it yourself: " + " ".join(cmd))
122
+ hint="run it yourself: " + " ".join(cmd)) from e
123
123
  if proc.returncode != 0:
124
124
  tail = (proc.stderr or proc.stdout or "").strip().splitlines()
125
125
  raise BoostError(
@@ -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:
@@ -72,7 +72,7 @@ def cmd_config(argv) -> int:
72
72
  except TypeError as e:
73
73
  raise BoostError(str(e),
74
74
  hint="the parent key holds a plain value — "
75
- "`boost config unset` it first")
75
+ "`boost config unset` it first") from e
76
76
  val = config.get(args.key)
77
77
  journal.log("config", args.key, op="set")
78
78
  out.ok("set %s = %s"
@@ -101,25 +101,31 @@ def cmd_clean(argv) -> int:
101
101
  help="also remove snapshots older than 90 days")
102
102
  args = p.parse_args(argv)
103
103
 
104
- items = [] # (path, kind, bytes)
105
- for _agent, spec in agents.known_agents().items():
104
+ items: list[tuple[Path, str, int]] = [] # (path, kind, bytes)
105
+ for spec in agents.known_agents().values():
106
106
  adir = spec["dir"]
107
107
  if not adir.is_dir():
108
108
  continue
109
- for link in sorted(adir.iterdir()):
110
- if link.is_symlink() and not link.exists():
111
- items.append((link, "broken symlink", 0))
109
+ items.extend(
110
+ (link, "broken symlink", 0)
111
+ for link in sorted(adir.iterdir())
112
+ if link.is_symlink() and not link.exists()
113
+ )
112
114
 
113
115
  configured = {t.safe_name for t in registry.list_taps()}
114
116
  if paths.cache_dir().is_dir():
115
- for f in sorted(paths.cache_dir().glob("*.json")):
116
- if f.stem not in configured:
117
- items.append((f, "stale tap cache", f.stat().st_size))
117
+ items.extend(
118
+ (f, "stale tap cache", f.stat().st_size)
119
+ for f in sorted(paths.cache_dir().glob("*.json"))
120
+ if f.stem not in configured
121
+ )
118
122
 
119
123
  if paths.lock_history_dir().is_dir():
120
124
  snaps = sorted(paths.lock_history_dir().glob("lock-*.json"))
121
- for old in snaps[:-50]:
122
- items.append((old, "old lock history", old.stat().st_size))
125
+ items.extend(
126
+ (old, "old lock history", old.stat().st_size)
127
+ for old in snaps[:-50]
128
+ )
123
129
 
124
130
  if paths.store_dir().is_dir():
125
131
  for pth in sorted(paths.store_dir().rglob("*")):
@@ -152,7 +158,7 @@ def cmd_clean(argv) -> int:
152
158
  if pth.is_symlink() or pth.is_file():
153
159
  pth.unlink()
154
160
  elif pth.is_dir():
155
- shutil.rmtree(pth)
161
+ util.rmtree(pth)
156
162
  except OSError as e:
157
163
  out.warn("could not remove %s: %s" % (_tilde(pth), e))
158
164
  continue
@@ -224,7 +230,7 @@ def cmd_create(argv) -> int:
224
230
  title = name.replace("-", " ").title()
225
231
  target.mkdir(parents=True, exist_ok=True)
226
232
  skill_md.write_text(frontmatter.dump(meta) + "\n\n"
227
- + _CREATE_BODY % {"title": title})
233
+ + _CREATE_BODY % {"title": title}, encoding="utf-8")
228
234
  journal.log("create", name, path=str(target))
229
235
  out.ok("created %s" % _tilde(skill_md))
230
236
  if args.install:
@@ -426,7 +432,7 @@ def cmd_onboard(argv) -> int:
426
432
  for rel, content in files:
427
433
  fp = repo / rel
428
434
  fp.parent.mkdir(parents=True, exist_ok=True)
429
- fp.write_text(content)
435
+ fp.write_text(content, encoding="utf-8")
430
436
  out.ok("created %s" % _tilde(fp))
431
437
  journal.log("onboard", _tilde(repo), pr=args.pr or None)
432
438
 
@@ -442,7 +448,7 @@ def cmd_onboard(argv) -> int:
442
448
  text=True, timeout=120)
443
449
  except (subprocess.TimeoutExpired, OSError) as e:
444
450
  raise BoostError("gh pr create failed: %s" % e,
445
- hint="branch %s is committed — push it and open the PR manually" % branch)
451
+ hint="branch %s is committed — push it and open the PR manually" % branch) from e
446
452
  if proc.returncode != 0:
447
453
  tail = (proc.stderr or proc.stdout or "").strip().splitlines()
448
454
  raise BoostError("gh pr create failed: %s" % (tail[-1] if tail else "unknown error"),
@@ -599,7 +605,7 @@ def cmd_schedule(argv) -> int:
599
605
  if plist.exists():
600
606
  present = True
601
607
  m = re.search(r"<key>StartInterval</key>\s*<integer>(\d+)</integer>",
602
- plist.read_text())
608
+ plist.read_text(encoding="utf-8"))
603
609
  if m:
604
610
  secs = int(m.group(1))
605
611
  interval = _interval_label(secs)
@@ -641,7 +647,7 @@ def cmd_schedule(argv) -> int:
641
647
  if darwin:
642
648
  plist = _plist_path()
643
649
  plist.parent.mkdir(parents=True, exist_ok=True)
644
- plist.write_text(_plist_body(shim, seconds))
650
+ plist.write_text(_plist_body(shim, seconds), encoding="utf-8")
645
651
  out.ok("wrote %s" % _tilde(plist))
646
652
  try:
647
653
  subprocess.run(["launchctl", "unload", str(plist)],
@@ -669,7 +675,7 @@ def cmd_schedule(argv) -> int:
669
675
  kept = [ln for ln in lines if not ln.rstrip().endswith(_CRON_MARK)]
670
676
  try:
671
677
  proc = subprocess.run(["crontab", "-"],
672
- input="\n".join(kept + [entry]) + "\n",
678
+ input="\n".join([*kept, entry]) + "\n",
673
679
  capture_output=True, text=True, timeout=30)
674
680
  except (OSError, subprocess.TimeoutExpired):
675
681
  proc = None
@@ -943,7 +949,7 @@ def cmd_mcp(argv) -> int:
943
949
  proc = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
944
950
  except (OSError, subprocess.TimeoutExpired) as e:
945
951
  raise BoostError("claude mcp %s failed: %s" % (args.action, e),
946
- hint="run it yourself: " + " ".join(cmd))
952
+ hint="run it yourself: " + " ".join(cmd)) from e
947
953
  for ln in (proc.stdout or "").strip().splitlines():
948
954
  out.info(ln)
949
955
  if proc.returncode != 0:
@@ -22,6 +22,7 @@ from ..core import (agents, ai, catalog, config, dense, embed, gitutil,
22
22
  from ..core import output as out
23
23
  from ..core.stackprobe import detect_stack # re-exported: shared with Quality
24
24
  from ..errors import BoostError
25
+ import contextlib
25
26
 
26
27
  __all__ = ["detect_stack"]
27
28
 
@@ -31,7 +32,7 @@ def _positive_int(s: str) -> int:
31
32
  try:
32
33
  v = int(s)
33
34
  except ValueError:
34
- raise argparse.ArgumentTypeError("invalid int value: %r" % s)
35
+ raise argparse.ArgumentTypeError("invalid int value: %r" % s) from None
35
36
  if v < 1:
36
37
  raise argparse.ArgumentTypeError("must be >= 1")
37
38
  return v
@@ -219,7 +220,7 @@ def cmd_index(argv):
219
220
  "search/code?q=filename:SKILL.md&per_page=100&page=%d" % page],
220
221
  capture_output=True, text=True, timeout=120)
221
222
  except (subprocess.TimeoutExpired, OSError) as e:
222
- raise BoostError("gh api timed out on page %d" % page, hint=str(e))
223
+ raise BoostError("gh api timed out on page %d" % page, hint=str(e)) from e
223
224
  if proc.returncode != 0:
224
225
  tail = "\n".join((proc.stderr or proc.stdout or "").strip()
225
226
  .splitlines()[-3:])
@@ -233,7 +234,7 @@ def cmd_index(argv):
233
234
  data = json.loads(proc.stdout)
234
235
  except json.JSONDecodeError:
235
236
  raise BoostError("gh api returned unparseable JSON",
236
- hint="try `boost index` again, or `gh auth status`")
237
+ hint="try `boost index` again, or `gh auth status`") from None
237
238
  if page == 1:
238
239
  total = int(data.get("total_count") or 0)
239
240
  batch = data.get("items") or []
@@ -252,7 +253,7 @@ def cmd_index(argv):
252
253
  paths.ensure_dirs()
253
254
  _discovery_path().write_text(json.dumps(
254
255
  {"generated": util.now_iso(), "github_total": total, "items": items},
255
- indent=1))
256
+ indent=1), encoding="utf-8")
256
257
  repos = len({it["repo"] for it in items})
257
258
  out.ok("indexed %d skill files across %d repos (GitHub reports %d total)"
258
259
  % (len(items), repos, total))
@@ -325,10 +326,10 @@ def cmd_discover(argv):
325
326
  "gh auth login`), then run `boost index`")
326
327
  return 0
327
328
  try:
328
- data = json.loads(dpath.read_text())
329
+ data = json.loads(dpath.read_text(encoding="utf-8"))
329
330
  except (json.JSONDecodeError, OSError):
330
331
  raise BoostError("the discovery index is corrupt",
331
- hint="rebuild it with `boost index`")
332
+ hint="rebuild it with `boost index`") from None
332
333
  all_items = data.get("items") or []
333
334
  tokens = [t.lower() for t in args.query if t.strip()]
334
335
  items = [it for it in all_items
@@ -598,7 +599,7 @@ def _aurora_pairs(curses):
598
599
  """Allocate Aurora curses colour pairs; token-name -> attr int (0 if none)."""
599
600
  names = ("cyan", "violet", "pink", "green", "yellow")
600
601
  try:
601
- custom = curses.can_change_color() and curses.COLORS >= 16 + len(names)
602
+ custom = curses.can_change_color() and 16 + len(names) <= curses.COLORS
602
603
  except curses.error:
603
604
  custom = False
604
605
  fg = dict(_nearest_base(curses))
@@ -695,19 +696,15 @@ def _browse_tui(curses, entries):
695
696
  return "loading description…"
696
697
 
697
698
  def ui(scr):
698
- try:
699
+ with contextlib.suppress(curses.error):
699
700
  curses.curs_set(0)
700
- except curses.error:
701
- pass
702
701
  scr.timeout(80) # short poll so a finished lazy-load redraws promptly
703
702
  th = _aurora_theme(curses)
704
703
 
705
704
  def put(y, x, s, attr=0): # clip-safe cell writer
706
705
  if 0 <= y < _h and 0 <= x < _w - 1 and s:
707
- try:
706
+ with contextlib.suppress(curses.error):
708
707
  scr.addnstr(y, x, s, _w - 1 - x, attr)
709
- except curses.error:
710
- pass
711
708
 
712
709
  filt, sel, detail = "", 0, False
713
710
  selected: set = set()
@@ -979,7 +976,7 @@ def cmd_count(argv):
979
976
  dpath = _discovery_path()
980
977
  if dpath.exists():
981
978
  try:
982
- discovery = len(json.loads(dpath.read_text()).get("items") or [])
979
+ discovery = len(json.loads(dpath.read_text(encoding="utf-8")).get("items") or [])
983
980
  except (json.JSONDecodeError, OSError):
984
981
  discovery = None
985
982
  if args.as_json:
@@ -31,7 +31,7 @@ def _read(p: Path) -> str:
31
31
  try:
32
32
  return Path(p).read_text(encoding="utf-8", errors="replace")
33
33
  except OSError as e:
34
- raise BoostError("cannot read %s: %s" % (_tilde(p), e))
34
+ raise BoostError("cannot read %s: %s" % (_tilde(p), e)) from e
35
35
 
36
36
 
37
37
  def _resolve_skill_md(name: str):
@@ -115,7 +115,7 @@ def _kind_table(heading, items, extra=None):
115
115
  row.append(str(e.get(extra[1], "") or ""))
116
116
  rows.append(tuple(row))
117
117
  if extra:
118
- headers = headers + (extra[0],)
118
+ headers = (*headers, extra[0])
119
119
  out.table(rows, headers=headers)
120
120
  noun = heading.split()[-1][:-1] # "installed rules" -> "rule"
121
121
  print(" " + out.aurora("%d %s%s installed"
@@ -319,10 +319,10 @@ def cmd_edit(argv):
319
319
  editor = os.environ.get("VISUAL") or os.environ.get("EDITOR") or "vi"
320
320
  cmd = shlex.split(editor) or ["vi"] # support EDITOR="code -w" etc.
321
321
  try:
322
- rc = subprocess.call(cmd + [str(path)])
322
+ rc = subprocess.call([*cmd, str(path)])
323
323
  except OSError as e:
324
324
  raise BoostError("cannot launch editor %r: %s" % (editor, e),
325
- hint="set $VISUAL or $EDITOR to a valid command")
325
+ hint="set $VISUAL or $EDITOR to a valid command") from e
326
326
  if rc != 0:
327
327
  out.warn("editor exited with status %d" % rc)
328
328
  sha = util.sha256_dir(sdir)
@@ -595,9 +595,11 @@ def cmd_deps(argv):
595
595
  unmet, pairs, seen = [], [], set()
596
596
  for name in sorted(inst):
597
597
  meta = _skill_meta(name) or {}
598
- for r in _as_list(meta.get("requires")):
599
- if r not in inst:
600
- unmet.append({"skill": name, "requires": r})
598
+ unmet.extend(
599
+ {"skill": name, "requires": r}
600
+ for r in _as_list(meta.get("requires"))
601
+ if r not in inst
602
+ )
601
603
  for c_name in _as_list(meta.get("conflicts")):
602
604
  if c_name in inst:
603
605
  key = tuple(sorted((name, c_name)))
@@ -22,6 +22,7 @@ from ..core import (ai, catalog, frontmatter, gitutil, imperative, journal,
22
22
  lockfile, paths, store, util)
23
23
  from ..core import output as out
24
24
  from ..errors import BoostError
25
+ import contextlib
25
26
 
26
27
  # ---------------------------------------------------------------- helpers
27
28
 
@@ -60,14 +61,14 @@ def _load_state(fname: str, default: dict) -> dict:
60
61
  if not p.exists():
61
62
  return json.loads(json.dumps(default))
62
63
  try:
63
- return json.loads(p.read_text())
64
+ return json.loads(p.read_text(encoding="utf-8"))
64
65
  except (json.JSONDecodeError, OSError):
65
66
  return json.loads(json.dumps(default))
66
67
 
67
68
 
68
69
  def _save_state(fname: str, data: dict) -> None:
69
70
  paths.ensure_dirs()
70
- (paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n")
71
+ (paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
71
72
 
72
73
 
73
74
  def _install_generated(name: str, text: str) -> None:
@@ -75,7 +76,7 @@ def _install_generated(name: str, text: str) -> None:
75
76
  with tempfile.TemporaryDirectory(prefix="boost-gen-") as td:
76
77
  src = Path(td) / name
77
78
  src.mkdir()
78
- (src / "SKILL.md").write_text(text)
79
+ (src / "SKILL.md").write_text(text, encoding="utf-8")
79
80
  res = store.install_from_path(src, name=name, tap_label="local")
80
81
  out.ok("installed %s → %s" % (name, _tilde(res.dest)))
81
82
  if res.linked:
@@ -88,7 +89,7 @@ def _write_generated(dest: Path, text: str) -> bool:
88
89
  out.info("aborted — %s left untouched" % _tilde(dest))
89
90
  return False
90
91
  dest.parent.mkdir(parents=True, exist_ok=True)
91
- dest.write_text(text)
92
+ dest.write_text(text, encoding="utf-8")
92
93
  out.ok("wrote %s" % _tilde(dest))
93
94
  return True
94
95
 
@@ -649,7 +650,7 @@ def cmd_evolve(argv: List[str]) -> int:
649
650
  if not args.apply:
650
651
  out.info(out.role("re-run with --apply to write these changes", "muted"))
651
652
  return 0
652
- skill_md.write_text(new)
653
+ skill_md.write_text(new, encoding="utf-8")
653
654
  entry["sha256"] = util.sha256_dir(store.skill_store_dir(args.name))
654
655
  entry["updated_at"] = util.now_iso()
655
656
  entry["version"] = new_ver
@@ -777,9 +778,8 @@ def cmd_context(argv: List[str]) -> int:
777
778
  inst = lockfile.installed()
778
779
  for name in sorted(_mentioned_skills(state)):
779
780
  entry = inst.get(name)
780
- if entry and not entry.get("quarantined"):
781
- if store.link_agents(name).linked:
782
- restored += 1
781
+ if entry and not entry.get("quarantined") and store.link_agents(name).linked:
782
+ restored += 1
783
783
  state["enabled"] = False
784
784
  _save_state(_CONTEXT_STATE, state)
785
785
  journal.log("context", "disable", restored=restored)
@@ -1005,10 +1005,8 @@ def _repo_activity(since: str) -> Tuple[Optional[int], Optional[int]]:
1005
1005
  proc = gitutil.run(["rev-list", "--count", "--since=" + since, "HEAD"],
1006
1006
  cwd=Path.cwd(), check=False)
1007
1007
  if proc.returncode == 0:
1008
- try:
1008
+ with contextlib.suppress(ValueError):
1009
1009
  commits = int(proc.stdout.strip() or 0)
1010
- except ValueError:
1011
- pass
1012
1010
  proc = gitutil.run(["log", "--name-only", "--since=" + since,
1013
1011
  "--pretty=format:"], cwd=Path.cwd(), check=False)
1014
1012
  if proc.returncode == 0:
@@ -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,10 +565,10 @@ 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
- hint="check the path exists and is writable")
571
+ hint="check the path exists and is writable") from e
579
572
  n_taps = sum(1 for ln in text.splitlines() if ln.startswith("tap "))
580
573
  n_skills = sum(1 for ln in text.splitlines() if ln.startswith("skill "))
581
574
  out.ok("wrote %s (%s, %s)" % (_tilde(dest), _plural(n_taps, "tap"),
@@ -596,9 +589,9 @@ 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
- raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e))
594
+ raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e)) from e
602
595
  taps_added = installed_n = present = failed = 0
603
596
  have_taps = {t.name for t in registry.list_taps()}
604
597
  have_skills = set(lockfile.installed())
@@ -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", ""),
@@ -936,21 +929,21 @@ def _snapshot_restore(snap_id: str) -> int:
936
929
  # Read the whole archive BEFORE touching the store, so a corrupt
937
930
  # snapshot can never leave us with an emptied environment.
938
931
  try:
939
- tf = tarfile.open(str(tar_path), "r:gz")
932
+ tf = tarfile.open(str(tar_path), "r:gz") # noqa: SIM115 (opened in a try to translate the error; used via `with tf:` below)
940
933
  except (tarfile.TarError, OSError, EOFError) as e:
941
934
  raise BoostError("snapshot %s is unreadable: %s" % (snap_id, e),
942
935
  hint="the store was left untouched — "
943
- "see `boost snapshot list` for other snapshots")
936
+ "see `boost snapshot list` for other snapshots") from e
944
937
  with tf:
945
938
  try:
946
939
  members = tf.getmembers()
947
940
  except (tarfile.TarError, OSError, EOFError) as e:
948
941
  raise BoostError("snapshot %s is corrupt: %s" % (snap_id, e),
949
942
  hint="the store was left untouched — "
950
- "see `boost snapshot list` for other snapshots")
943
+ "see `boost snapshot list` for other snapshots") from e
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()
@@ -1019,7 +1012,7 @@ def cmd_export(argv: List[str]) -> int:
1019
1012
  tf.add(str(store.skill_store_dir(name)), arcname=name)
1020
1013
  except OSError as e:
1021
1014
  raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
1022
- hint="check the output path exists and is writable")
1015
+ hint="check the output path exists and is writable") from e
1023
1016
  out.ok("exported %s → %s (%s)" % (_plural(len(chosen), "skill"),
1024
1017
  _tilde(dest.resolve()),
1025
1018
  util.human_size(dest.stat().st_size)))
@@ -1070,7 +1063,7 @@ def cmd_adapt(argv: List[str]) -> int:
1070
1063
  util.atomic_write_text(dest, source)
1071
1064
  except OSError as e:
1072
1065
  raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
1073
- hint="check the output path exists and is writable")
1066
+ hint="check the output path exists and is writable") from e
1074
1067
  out.ok("adapted %s → %s (%s · %s)" % (display, _tilde(dest.resolve()),
1075
1068
  args.to, model or "framework default"))
1076
1069
  else: