boost-skill-cli 1.0.78__tar.gz → 1.0.80__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 (230) hide show
  1. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/info.py +11 -10
  4. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/catalog.py +20 -3
  5. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/logs.py +10 -2
  6. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  7. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/SOURCES.txt +2 -0
  8. boost_skill_cli-1.0.80/docs/roadmap/items/fork-safe-network-proxy-handler.md +26 -0
  9. boost_skill_cli-1.0.80/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +23 -0
  10. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/robust-tag-argument-parsing.md +3 -3
  11. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +3 -3
  12. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap.html +45 -4
  13. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_info.py +24 -0
  14. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_catalog.py +34 -0
  15. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_logs.py +13 -0
  16. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/.gitattributes +0 -0
  17. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/.gitignore +0 -0
  18. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/CLAUDE.md +0 -0
  19. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/CONTRIBUTING.md +0 -0
  20. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/LICENSE +0 -0
  21. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/MANIFEST.in +0 -0
  22. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/Makefile +0 -0
  23. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/README.md +0 -0
  24. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/SECURITY.md +0 -0
  25. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost +0 -0
  26. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/__init__.py +0 -0
  27. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/__main__.py +0 -0
  28. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/cli.py +0 -0
  29. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/cliparse.py +0 -0
  30. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/__init__.py +0 -0
  31. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/bmad.py +0 -0
  32. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/configuration.py +0 -0
  33. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/discovery.py +0 -0
  34. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/hooks.py +0 -0
  35. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/intelligence.py +0 -0
  36. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/pkg.py +0 -0
  37. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/quality.py +0 -0
  38. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/taps.py +0 -0
  39. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/commands/team.py +0 -0
  40. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/__init__.py +0 -0
  41. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/agents.py +0 -0
  42. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/ai.py +0 -0
  43. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/claude_settings.py +0 -0
  44. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/config.py +0 -0
  45. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/dense.py +0 -0
  46. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/embed.py +0 -0
  47. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/frontmatter.py +0 -0
  48. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/gitutil.py +0 -0
  49. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/journal.py +0 -0
  50. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/lockfile.py +0 -0
  51. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/mcp.py +0 -0
  52. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/output.py +0 -0
  53. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/paths.py +0 -0
  54. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/policy.py +0 -0
  55. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/rag.py +0 -0
  56. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/registry.py +0 -0
  57. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/store.py +0 -0
  58. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/core/util.py +0 -0
  59. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/data/registries.json +0 -0
  60. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/errors.py +0 -0
  61. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_cli/spin.py +0 -0
  62. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  63. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  64. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/requires.txt +0 -0
  65. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/boost_skill_cli.egg-info/top_level.txt +0 -0
  66. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/.claude/settings.local.json +0 -0
  67. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/DEBUGGING.md +0 -0
  68. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/demo.gif +0 -0
  69. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/demo.tape +0 -0
  70. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/design-roadmap.html +0 -0
  71. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/index.html +0 -0
  72. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/mcp-hub.html +0 -0
  73. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/rag-architecture.md +0 -0
  74. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D01.md +0 -0
  75. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D02.md +0 -0
  76. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D03.md +0 -0
  77. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D04.md +0 -0
  78. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D05.md +0 -0
  79. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D06.md +0 -0
  80. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D07.md +0 -0
  81. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D08.md +0 -0
  82. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D09.md +0 -0
  83. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D10.md +0 -0
  84. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D11.md +0 -0
  85. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D12.md +0 -0
  86. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D13.md +0 -0
  87. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D14.md +0 -0
  88. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D15.md +0 -0
  89. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D16.md +0 -0
  90. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D17.md +0 -0
  91. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D18.md +0 -0
  92. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D19.md +0 -0
  93. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D20.md +0 -0
  94. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D21.md +0 -0
  95. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D22.md +0 -0
  96. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D23.md +0 -0
  97. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D24.md +0 -0
  98. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D25.md +0 -0
  99. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/BOOST-D26.md +0 -0
  100. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  101. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  102. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  103. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  104. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  105. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  106. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  107. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  108. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  109. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  110. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  111. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  112. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  113. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  114. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  115. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  116. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  117. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  118. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  119. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  120. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  121. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  122. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  123. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  124. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  125. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  126. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  127. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  128. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  129. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  130. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  131. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  132. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  133. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  134. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  135. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  136. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  137. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  138. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  139. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  140. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  141. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  142. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  143. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  144. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  145. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  146. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  147. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  148. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  149. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  150. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  151. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  152. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  153. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  154. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  155. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  156. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  157. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  158. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  159. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  160. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  161. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  162. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  163. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  164. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  165. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  166. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  167. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  168. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  169. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  170. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  171. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  172. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  173. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  174. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  175. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  176. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  177. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  178. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  179. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  180. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  181. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/index.html +0 -0
  182. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/pyproject.toml +0 -0
  183. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/scripts/build_registries.py +0 -0
  184. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/scripts/build_roadmap.py +0 -0
  185. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/scripts/mutation_gate.py +0 -0
  186. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/setup.cfg +0 -0
  187. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/style/README.md +0 -0
  188. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/style/boost.css +0 -0
  189. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/style/boost.js +0 -0
  190. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/style/demo.html +0 -0
  191. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/conftest.py +0 -0
  192. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_bmad.py +0 -0
  193. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_configuration.py +0 -0
  194. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_discovery.py +0 -0
  195. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_hooks.py +0 -0
  196. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_intelligence.py +0 -0
  197. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_pkg.py +0 -0
  198. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_quality.py +0 -0
  199. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_taps.py +0 -0
  200. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_cli_team.py +0 -0
  201. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/functional/test_everyday_loop.py +0 -0
  202. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/make_fixture.py +0 -0
  203. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/smoke.sh +0 -0
  204. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_agents.py +0 -0
  205. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_ai.py +0 -0
  206. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_claude_settings.py +0 -0
  207. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_cliparse.py +0 -0
  208. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_config.py +0 -0
  209. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_dense.py +0 -0
  210. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_dense_fallback.py +0 -0
  211. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_embed.py +0 -0
  212. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_errors_and_cli_table.py +0 -0
  213. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_frontmatter.py +0 -0
  214. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_gitutil.py +0 -0
  215. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_journal.py +0 -0
  216. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_lockfile.py +0 -0
  217. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_mcp.py +0 -0
  218. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_mutation_hardening.py +0 -0
  219. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_output.py +0 -0
  220. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_paths.py +0 -0
  221. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_policy.py +0 -0
  222. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_rag.py +0 -0
  223. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_registries_fresh.py +0 -0
  224. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_registry.py +0 -0
  225. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_roadmap_fresh.py +0 -0
  226. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_spin.py +0 -0
  227. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_store.py +0 -0
  228. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_token_parity.py +0 -0
  229. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_util.py +0 -0
  230. {boost_skill_cli-1.0.78 → boost_skill_cli-1.0.80}/tests/unit/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.78
3
+ Version: 1.0.80
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -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.78'
22
- __version_tuple__ = version_tuple = (1, 0, 78)
21
+ __version__ = version = '1.0.80'
22
+ __version_tuple__ = version_tuple = (1, 0, 80)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -591,16 +591,17 @@ def cmd_tag(argv):
591
591
  ap.add_argument("--json", action="store_true", help="machine-readable output")
592
592
  ap.add_argument("name", nargs="?", help="installed skill")
593
593
  ap.add_argument("mods", nargs="*", help="+tag to add, -tag to remove")
594
- args, extras = ap.parse_known_args(argv)
595
- # '-tag' tokens land in extras; re-order the combined pool to match the
596
- # user's original argv so `-x +x` nets differently from `+x -x`.
597
- remaining = list(args.mods) + extras
598
- mods = []
599
- for tok in argv:
600
- if tok in remaining:
601
- mods.append(tok)
602
- remaining.remove(tok)
603
- mods += remaining # anything unmatched keeps parse order (defensive)
594
+ # A `-tag` removal looks like an option to argparse, which forced the old
595
+ # parse_known_args + argv re-walk. Instead split the literal option strings
596
+ # from operands ourselves: the skill name and its +tag/-tag operands keep
597
+ # their exact argv order with no re-walk, so `-x +x` still nets differently
598
+ # from `+x -x`.
599
+ flag_strings = {"--list", "--json", "-h", "--help"}
600
+ flags = [t for t in argv if t in flag_strings]
601
+ operands = [t for t in argv if t not in flag_strings]
602
+ args = ap.parse_args(flags)
603
+ args.name = operands[0] if operands else None
604
+ mods = operands[1:]
604
605
 
605
606
  if args.list_all:
606
607
  mapping = {}
@@ -67,10 +67,12 @@ def _description(meta: dict, body: str) -> str:
67
67
  def _make_entry(root: Path, defining_file: Path, kind: str, default_name: str,
68
68
  tap_name: str, curated: bool, meta: dict, body: str) -> dict:
69
69
  name = str(meta.get("name") or "").strip() or default_name
70
+ slug = util.slugify(name) if " " in name else name
71
+ description = _description(meta, body)
70
72
  item_dir = defining_file.parent
71
73
  return {
72
- "name": util.slugify(name) if " " in name else name,
73
- "description": _description(meta, body),
74
+ "name": slug,
75
+ "description": description,
74
76
  "version": str(meta.get("version") or "0.0.0"),
75
77
  "tap": tap_name,
76
78
  "curated": curated,
@@ -78,6 +80,9 @@ def _make_entry(root: Path, defining_file: Path, kind: str, default_name: str,
78
80
  "rel_dir": str(item_dir.relative_to(root)) if item_dir != root else ".",
79
81
  "skill_md": str(defining_file.relative_to(root)),
80
82
  "meta": meta,
83
+ # Lowercased substring-search blob, flattened once at index time so
84
+ # search() never re-walks the frontmatter per query (see _search_blob).
85
+ "search_blob": _search_blob(slug, description, meta),
81
86
  }
82
87
 
83
88
 
@@ -232,6 +237,13 @@ def _meta_text(meta) -> str:
232
237
  return " ".join(parts).lower()
233
238
 
234
239
 
240
+ def _search_blob(name: str, description: str, meta) -> str:
241
+ """The full lowercased substring-search text for an entry: its name,
242
+ description and flattened frontmatter. Built once at index time and cached
243
+ on the entry as ``search_blob`` so search() never rebuilds it per query."""
244
+ return " ".join([name.lower(), (description or "").lower(), _meta_text(meta)])
245
+
246
+
235
247
  def search(query: str, entries: Optional[List[dict]] = None):
236
248
  """Rank entries against a query -> [(entry, score)] best-first."""
237
249
  entries = all_entries() if entries is None else entries
@@ -241,7 +253,12 @@ def search(query: str, entries: Optional[List[dict]] = None):
241
253
  for e in entries:
242
254
  name = e["name"].lower()
243
255
  desc = (e["description"] or "").lower()
244
- blob = " ".join([name, desc, _meta_text(e.get("meta", {}))])
256
+ # Precomputed at index time; fall back for older caches / entries
257
+ # constructed without a blob (e.g. tests passing raw dicts).
258
+ blob = e.get("search_blob")
259
+ if blob is None:
260
+ blob = _search_blob(e["name"], e.get("description", ""),
261
+ e.get("meta", {}))
245
262
  score = 0
246
263
  if q == name:
247
264
  score += 100
@@ -170,8 +170,16 @@ def configure(verbose: bool = False, debug: bool = False,
170
170
 
171
171
 
172
172
  def log_invocation(argv: List[str]) -> None:
173
- """Record a command invocation at the head of the trail."""
174
- get_logger().info("invoke: boost %s", " ".join(argv))
173
+ """Record a command invocation at the head of the trail.
174
+
175
+ Includes ``pid``/``ppid`` and the interpreter path so a *native* crash —
176
+ which Python can't catch, so it never reaches :func:`write_crash_report` —
177
+ can still be correlated against an OS crash report by PID. If an OS report
178
+ names a PID that never appears here, boost is ruled out as that process.
179
+ """
180
+ get_logger().info(
181
+ "invoke: boost %s [pid=%d ppid=%d py=%s]",
182
+ " ".join(argv), os.getpid(), os.getppid(), sys.executable)
175
183
 
176
184
 
177
185
  def log_completion(argv: List[str], rc: int, elapsed_ms: float) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.78
3
+ Version: 1.0.80
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -115,11 +115,13 @@ docs/roadmap/items/extension-free-tests-core-dense-py.md
115
115
  docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md
116
116
  docs/roadmap/items/finish-mutation-hardening-across-core.md
117
117
  docs/roadmap/items/fix-self-update-version-detection-dead-branch.md
118
+ docs/roadmap/items/fork-safe-network-proxy-handler.md
118
119
  docs/roadmap/items/frontmatter-scalar-over-coercion.md
119
120
  docs/roadmap/items/github-pages-deploy-is-broken-every-push.md
120
121
  docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md
121
122
  docs/roadmap/items/html-validation-html-validate.md
122
123
  docs/roadmap/items/integrity-verification-boost-verify.md
124
+ docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md
123
125
  docs/roadmap/items/layering-guard-import-linter.md
124
126
  docs/roadmap/items/lighthouse-ci-on-the-pages-site.md
125
127
  docs/roadmap/items/localize-the-stored-bm25-snippet.md
@@ -0,0 +1,26 @@
1
+ ---
2
+ id: fork-safe-network-proxy-handler
3
+ board: code
4
+ section: health
5
+ status: planned
6
+ category: Reliability · Network
7
+ complexity: S
8
+ impact: Med
9
+ wow: 3
10
+ note: avoid macOS _scproxy Obj-C path
11
+ order: 9
12
+ owner:
13
+ pr:
14
+ title: Fork-safe network layer — explicit <code>ProxyHandler</code>
15
+ ---
16
+ <code>core/ai.py</code> and <code>core/embed.py</code> call
17
+ <code>urllib.request.urlopen</code> with the default opener, which on
18
+ macOS runs <code>getproxies_macosx_sysconf()</code> →
19
+ <code>_scproxy</code> → SystemConfiguration/CoreFoundation — an
20
+ Obj-C path that is <strong>not fork-safe</strong> and aborts on the
21
+ child side of a <code>fork()</code>. Harmless in today's single-process
22
+ CLI, but fragile if boost ever runs post-fork (a future multiprocessing
23
+ worker, or an embedding host). Build the opener with an explicit
24
+ <code>ProxyHandler</code> honoring <code>HTTP(S)_PROXY</code>/<code>NO_PROXY</code>
25
+ so network calls never touch the Obj-C proxy machinery — deterministic
26
+ and fork-safe.
@@ -0,0 +1,23 @@
1
+ ---
2
+ id: invocation-pid-logging-for-crash-correlation
3
+ board: code
4
+ section: shipped
5
+ status: shipped
6
+ category: Observability · Diagnostics
7
+ complexity: S
8
+ impact: Med
9
+ wow: 3
10
+ note: rule boost in/out of OS crash reports
11
+ order: 9
12
+ owner:
13
+ pr:
14
+ title: Crash-correlation breadcrumbs in the invocation log
15
+ ---
16
+ A native abort (e.g. a macOS Obj-C fork-safety <code>SIGABRT</code>) kills the
17
+ process outright, so it never reaches boost's Python crash recorder —
18
+ leaving no boost-side trace to confirm or clear boost when an OS crash
19
+ report appears. <code>log_invocation</code> now records
20
+ <code>pid</code>, <code>ppid</code> and the interpreter path on every
21
+ run, so any OS crash report can be cross-referenced by PID: if the
22
+ crashing PID never appears in <code>~/.boost/logs/boost.log</code>,
23
+ boost is definitively ruled out.
@@ -2,15 +2,15 @@
2
2
  id: robust-tag-argument-parsing
3
3
  board: code
4
4
  section: internals
5
- status: planned
5
+ status: shipped
6
6
  category: Maintainability
7
7
  complexity: M
8
8
  impact: Med
9
9
  wow: 2
10
10
  note:
11
11
  order: 16
12
- owner:
13
- pr:
12
+ owner: loop/robust-tag-parsing
13
+ pr: 94
14
14
  title: Robust <code>tag</code> argument parsing
15
15
  ---
16
16
  Because <code>-tag</code> looks like an option, <code>cmd_tag</code> calls <code>parse_known_args</code> then re-walks raw <code>argv</code> to reorder tokens (<code>info.py:575–584</code>) — fragile enough that the comment calls it "defensive". Replace with a positional-only sub-parser or an explicit <code>--add/--remove</code> design.
@@ -2,15 +2,15 @@
2
2
  id: stop-re-serializing-entry-meta-on-every-search
3
3
  board: code
4
4
  section: internals
5
- status: planned
5
+ status: shipped
6
6
  category: Performance
7
7
  complexity: S
8
8
  impact: Med
9
9
  wow: 2
10
10
  note:
11
11
  order: 11
12
- owner:
13
- pr:
12
+ owner: loop/precompute-search-blob
13
+ pr: 93
14
14
  title: Stop re-serializing entry meta on every search
15
15
  ---
16
16
  <code>catalog.search</code> computes <code>json.dumps(meta).lower()</code> for <b>every entry on every query</b> just to substring-match (<code>catalog.py:222</code>). Precompute a lowercased search blob at index time, or match structured fields directly.
@@ -115,10 +115,10 @@
115
115
  <section class="block reveal">
116
116
  <div class="stats">
117
117
  <!-- ROADMAP:stats -->
118
- <div class="stat"><b>8</b><span>Shipped</span></div>
118
+ <div class="stat"><b>9</b><span>Shipped</span></div>
119
119
  <div class="stat"><b>3</b><span>Next up</span></div>
120
120
  <div class="stat"><b>4</b><span>Planned</span></div>
121
- <div class="stat"><b>83</b><span>Loop finds</span></div>
121
+ <div class="stat"><b>85</b><span>Loop finds</span></div>
122
122
  <!-- /ROADMAP:stats -->
123
123
  </div>
124
124
  </section>
@@ -270,6 +270,25 @@
270
270
  <span class="m">transport allow-list + -- guard</span>
271
271
  </div>
272
272
  </article>
273
+
274
+ <article class="cap rcard">
275
+ <div class="head"><span class="pill shipped">Shipped</span><span class="cat">Observability · Diagnostics</span></div>
276
+ <h3>Crash-correlation breadcrumbs in the invocation log</h3>
277
+ <p>A native abort (e.g. a macOS Obj-C fork-safety <code>SIGABRT</code>) kills the
278
+ process outright, so it never reaches boost's Python crash recorder —
279
+ leaving no boost-side trace to confirm or clear boost when an OS crash
280
+ report appears. <code>log_invocation</code> now records
281
+ <code>pid</code>, <code>ppid</code> and the interpreter path on every
282
+ run, so any OS crash report can be cross-referenced by PID: if the
283
+ crashing PID never appears in <code>~/.boost/logs/boost.log</code>,
284
+ boost is definitively ruled out.</p>
285
+ <div class="meta">
286
+ <span class="m">Complexity <b>S</b></span>
287
+ <span class="m">Impact <b>Med</b></span>
288
+ <span class="m">Wow <b class="wow">★★★</b></span>
289
+ <span class="m">rule boost in/out of OS crash reports</span>
290
+ </div>
291
+ </article>
273
292
  <!-- /ROADMAP:cards -->
274
293
 
275
294
  </div>
@@ -498,7 +517,7 @@
498
517
  </article>
499
518
 
500
519
  <article class="cap rcard">
501
- <div class="head"><span class="pill planned">Planned</span><span class="cat">Performance</span></div>
520
+ <div class="head"><span class="pill shipped">Shipped</span><span class="cat">Performance</span></div>
502
521
  <h3>Stop re-serializing entry meta on every search</h3>
503
522
  <p><code>catalog.search</code> computes <code>json.dumps(meta).lower()</code> for <b>every entry on every query</b> just to substring-match (<code>catalog.py:222</code>). Precompute a lowercased search blob at index time, or match structured fields directly.</p>
504
523
  <div class="meta">
@@ -553,7 +572,7 @@
553
572
  </article>
554
573
 
555
574
  <article class="cap rcard">
556
- <div class="head"><span class="pill planned">Planned</span><span class="cat">Maintainability</span></div>
575
+ <div class="head"><span class="pill shipped">Shipped</span><span class="cat">Maintainability</span></div>
557
576
  <h3>Robust <code>tag</code> argument parsing</h3>
558
577
  <p>Because <code>-tag</code> looks like an option, <code>cmd_tag</code> calls <code>parse_known_args</code> then re-walks raw <code>argv</code> to reorder tokens (<code>info.py:575–584</code>) — fragile enough that the comment calls it "defensive". Replace with a positional-only sub-parser or an explicit <code>--add/--remove</code> design.</p>
559
578
  <div class="meta">
@@ -742,6 +761,28 @@
742
761
  <span class="m">stretch</span>
743
762
  </div>
744
763
  </article>
764
+
765
+ <article class="cap rcard">
766
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Reliability · Network</span></div>
767
+ <h3>Fork-safe network layer — explicit <code>ProxyHandler</code></h3>
768
+ <p><code>core/ai.py</code> and <code>core/embed.py</code> call
769
+ <code>urllib.request.urlopen</code> with the default opener, which on
770
+ macOS runs <code>getproxies_macosx_sysconf()</code> →
771
+ <code>_scproxy</code> → SystemConfiguration/CoreFoundation — an
772
+ Obj-C path that is <strong>not fork-safe</strong> and aborts on the
773
+ child side of a <code>fork()</code>. Harmless in today's single-process
774
+ CLI, but fragile if boost ever runs post-fork (a future multiprocessing
775
+ worker, or an embedding host). Build the opener with an explicit
776
+ <code>ProxyHandler</code> honoring <code>HTTP(S)_PROXY</code>/<code>NO_PROXY</code>
777
+ so network calls never touch the Obj-C proxy machinery — deterministic
778
+ and fork-safe.</p>
779
+ <div class="meta">
780
+ <span class="m">Complexity <b>S</b></span>
781
+ <span class="m">Impact <b>Med</b></span>
782
+ <span class="m">Wow <b class="wow">★★★</b></span>
783
+ <span class="m">avoid macOS _scproxy Obj-C path</span>
784
+ </div>
785
+ </article>
745
786
  <!-- /ROADMAP:cards -->
746
787
 
747
788
  </div>
@@ -330,3 +330,27 @@ class TestTag:
330
330
  assert len(events) == 1
331
331
  assert events[0]["subject"] == "brainstorming"
332
332
  assert events[0]["tags"] == ["kept"]
333
+
334
+ def test_operand_order_preserved_add_then_remove(self, boost, installed):
335
+ # `+x -x` in argv order: add then remove -> nets to no tag.
336
+ boost("tag", "brainstorming", "+x", "-x")
337
+ assert _lock()["brainstorming"].get("tags", []) == []
338
+
339
+ def test_operand_order_preserved_remove_then_add(self, boost, installed):
340
+ # `-x +x`: remove (no-op on absent) then add -> nets to the tag present.
341
+ boost("tag", "brainstorming", "-x", "+x")
342
+ assert _lock()["brainstorming"]["tags"] == ["x"]
343
+
344
+ def test_removal_token_not_parsed_as_option(self, boost, installed):
345
+ # `-alpha` looks like an option but must be treated as a removal.
346
+ boost("tag", "brainstorming", "+alpha", "+beta")
347
+ r = boost("tag", "brainstorming", "-alpha")
348
+ assert "#beta" in r.out and "#alpha" not in r.out
349
+ assert _lock()["brainstorming"]["tags"] == ["beta"]
350
+
351
+ def test_list_flag_after_name_still_lists(self, boost, tapped):
352
+ # `--list` anywhere is a flag, not an operand.
353
+ boost("install", "brainstorming")
354
+ boost("tag", "brainstorming", "+shared")
355
+ r = boost("tag", "--list")
356
+ assert "#shared" in r.out
@@ -477,3 +477,37 @@ class TestMetaText:
477
477
  def test_curated_entry_with_no_match_is_excluded(self):
478
478
  res = catalog.search("xyzzy", entries=[_entry("unrelated", "t", curated=True)])
479
479
  assert res == []
480
+
481
+
482
+ class TestSearchBlobPrecompute:
483
+ def test_search_blob_flattens_name_desc_meta_lowercased(self):
484
+ blob = catalog._search_blob(
485
+ "Foo-Bar", "Desc Here", {"tags": ["Alpha"], "x": "On"})
486
+ assert blob == "foo-bar desc here tags alpha x on"
487
+
488
+ def test_search_blob_tolerates_empty_description(self):
489
+ assert catalog._search_blob("name", "", {}) == "name "
490
+ assert catalog._search_blob("name", None, {}) == "name "
491
+
492
+ def test_make_entry_precomputes_blob(self, tmp_path):
493
+ f = tmp_path / "SKILL.md"
494
+ f.write_text("body")
495
+ e = catalog._make_entry(
496
+ tmp_path, f, "skill", "fallback-name", "tap", False,
497
+ {"tags": ["Kubernetes"]}, "Handles clusters")
498
+ assert e["search_blob"] == "fallback-name handles clusters tags kubernetes"
499
+
500
+ def test_search_reads_precomputed_blob_not_live_meta(self):
501
+ # Token lives only in search_blob, not in meta -> a match proves search
502
+ # consumed the precomputed blob rather than re-walking meta.
503
+ e = _entry("x", "t", meta={})
504
+ e["search_blob"] = "x zzsentinel"
505
+ res = catalog.search("zzsentinel", entries=[e])
506
+ assert [(m["name"], s) for m, s in res] == [("x", 2)]
507
+
508
+ def test_search_falls_back_when_blob_absent(self):
509
+ # Older caches / raw entries have no blob -> recompute from meta.
510
+ e = _entry("x", "t", meta={"tags": ["fallbackword"]})
511
+ assert "search_blob" not in e
512
+ res = catalog.search("fallbackword", entries=[e])
513
+ assert [(m["name"], s) for m, s in res] == [("x", 2)]
@@ -79,6 +79,19 @@ def test_invocation_is_logged(env):
79
79
  assert "invoke: boost install foo" in logs.log_path().read_text()
80
80
 
81
81
 
82
+ def test_invocation_records_pid_ppid_and_interpreter(env):
83
+ """PID/PPID + interpreter are logged so a native crash can be correlated
84
+ against an OS crash report (and boost ruled in or out) by PID."""
85
+ import os
86
+ import sys
87
+ logs.configure()
88
+ logs.log_invocation(["mcp", "--stdio"])
89
+ line = logs.log_path().read_text()
90
+ assert ("pid=%d" % os.getpid()) in line
91
+ assert ("ppid=%d" % os.getppid()) in line
92
+ assert ("py=%s" % sys.executable) in line
93
+
94
+
82
95
  def test_completion_logs_rc_and_duration(env):
83
96
  logs.configure()
84
97
  logs.log_completion(["count"], 0, 12.7)
File without changes