boost-skill-cli 1.0.67__tar.gz → 1.0.69__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.
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/configuration.py +14 -7
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/discovery.py +198 -21
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/SOURCES.txt +0 -1
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/design-roadmap.html +10 -2
- boost_skill_cli-1.0.69/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +25 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap.html +23 -24
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_configuration.py +37 -3
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_discovery.py +107 -1
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_output.py +15 -0
- boost_skill_cli-1.0.67/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -16
- boost_skill_cli-1.0.67/docs/roadmap/items/guard-output-table-against-ragged-rows.md +0 -16
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/.gitattributes +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/.gitignore +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/LICENSE +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/Makefile +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/README.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/index.html +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/index.html +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/setup.cfg +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/style/README.md +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/style/boost.css +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/style/boost.js +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/style/demo.html +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/unit/test_version.py +0 -0
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
21
|
+
__version__ = version = '1.0.69'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 69)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1147,14 +1147,21 @@ def cmd_self_update(argv) -> int:
|
|
|
1147
1147
|
raise BoostError("boost is not running from a git checkout",
|
|
1148
1148
|
hint="git clone the boost repo and symlink bin")
|
|
1149
1149
|
old = __version__
|
|
1150
|
+
before = gitutil.head_commit(root)
|
|
1150
1151
|
gitutil.run(["-C", str(root), "pull", "--ff-only"], timeout=120)
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1152
|
+
after = gitutil.head_commit(root)
|
|
1153
|
+
# The version is setuptools-scm derived — there is no `__version__ = "…"`
|
|
1154
|
+
# literal to grep, and the build-time _version.py / installed metadata are
|
|
1155
|
+
# stale until a reinstall. Ask git for the freshly-pulled version directly
|
|
1156
|
+
# (mirrors boost_cli._detect_version()'s git-checkout fallback), and treat a
|
|
1157
|
+
# moved HEAD as the unambiguous "an update landed" signal — independent of
|
|
1158
|
+
# how the two version strings happen to be formatted.
|
|
1159
|
+
new = gitutil.run(
|
|
1160
|
+
["-C", str(root), "describe", "--tags", "--always", "--dirty"],
|
|
1161
|
+
check=False, timeout=10).stdout.strip().lstrip("v") or old
|
|
1162
|
+
updated = bool(after) and after != before
|
|
1163
|
+
journal.log("self-update", new if updated else old, previous=old)
|
|
1164
|
+
if updated:
|
|
1158
1165
|
out.ok("boost v%s → v%s" % (old, new))
|
|
1159
1166
|
else:
|
|
1160
1167
|
out.ok("already up to date (v%s)" % old)
|
|
@@ -544,6 +544,140 @@ def _browse_plain(entries, why: str):
|
|
|
544
544
|
return 0
|
|
545
545
|
|
|
546
546
|
|
|
547
|
+
# --- Aurora palette for the curses browser -----------------------------------
|
|
548
|
+
# The full-screen browser paints itself in the same Aurora tokens as the rest
|
|
549
|
+
# of the CLI. curses can't reuse the ANSI escapes from core.output, so these
|
|
550
|
+
# helpers translate the single-source palette (out.TOKENS) into curses colour
|
|
551
|
+
# pairs, degrading to the 8 base colours — then to monochrome — when a terminal
|
|
552
|
+
# (or the test fake) can't do better.
|
|
553
|
+
|
|
554
|
+
def _curses_rgb1000():
|
|
555
|
+
"""out.TOKENS scaled from 0..255 to curses' 0..1000 RGB range.
|
|
556
|
+
|
|
557
|
+
Parity-locked to the web palette: whatever style/boost.css defines, the
|
|
558
|
+
browser renders, so the TUI can never drift from the design system.
|
|
559
|
+
"""
|
|
560
|
+
return {name: tuple(round(c / 255 * 1000) for c in rgb)
|
|
561
|
+
for name, rgb in out.TOKENS.items()}
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _nearest_base(curses):
|
|
565
|
+
"""Each Aurora token mapped to its closest of the 8 base curses colours.
|
|
566
|
+
|
|
567
|
+
Used when the terminal can't redefine colours (mirrors the 16-colour
|
|
568
|
+
fallback in core.output._AURORA).
|
|
569
|
+
"""
|
|
570
|
+
return {"cyan": curses.COLOR_CYAN, "violet": curses.COLOR_MAGENTA,
|
|
571
|
+
"pink": curses.COLOR_MAGENTA, "green": curses.COLOR_GREEN,
|
|
572
|
+
"yellow": curses.COLOR_YELLOW}
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def _match_positions(needle, hay):
|
|
576
|
+
"""Indices in hay consumed by a left-to-right subsequence match of needle.
|
|
577
|
+
|
|
578
|
+
Drives fuzzy-match highlighting; returns [] when needle isn't a
|
|
579
|
+
subsequence of hay (so nothing is highlighted).
|
|
580
|
+
"""
|
|
581
|
+
positions, i = [], 0
|
|
582
|
+
for j, ch in enumerate(hay):
|
|
583
|
+
if i < len(needle) and ch == needle[i]:
|
|
584
|
+
positions.append(j)
|
|
585
|
+
i += 1
|
|
586
|
+
return positions if i == len(needle) else []
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _scrollbar(total, rows, top):
|
|
590
|
+
"""(thumb_start, thumb_len) for a rows-tall scrollbar; None when all fits."""
|
|
591
|
+
if rows <= 0 or total <= rows:
|
|
592
|
+
return None
|
|
593
|
+
thumb = max(1, rows * rows // total)
|
|
594
|
+
max_top = total - rows
|
|
595
|
+
start = round((rows - thumb) * top / max_top) if max_top else 0
|
|
596
|
+
return (start, thumb)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _grad_segments(width, n=3):
|
|
600
|
+
"""Split a width-wide rule into n near-equal [(start, length)] runs.
|
|
601
|
+
|
|
602
|
+
Lets the browser paint a cyan -> violet -> pink gradient bar the same way
|
|
603
|
+
out.gradient() lerps the wordmark.
|
|
604
|
+
"""
|
|
605
|
+
if width <= 0:
|
|
606
|
+
return []
|
|
607
|
+
n = min(n, width)
|
|
608
|
+
base, extra = divmod(width, n)
|
|
609
|
+
segs, pos = [], 0
|
|
610
|
+
for k in range(n):
|
|
611
|
+
length = base + (1 if k < extra else 0)
|
|
612
|
+
segs.append((pos, length))
|
|
613
|
+
pos += length
|
|
614
|
+
return segs
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def _aurora_pairs(curses):
|
|
618
|
+
"""Allocate Aurora curses colour pairs; token-name -> attr int (0 if none)."""
|
|
619
|
+
names = ("cyan", "violet", "pink", "green", "yellow")
|
|
620
|
+
try:
|
|
621
|
+
custom = curses.can_change_color() and curses.COLORS >= 16 + len(names)
|
|
622
|
+
except curses.error:
|
|
623
|
+
custom = False
|
|
624
|
+
fg = dict(_nearest_base(curses))
|
|
625
|
+
if custom:
|
|
626
|
+
rgb = _curses_rgb1000()
|
|
627
|
+
for idx, nm in enumerate(names):
|
|
628
|
+
slot = 16 + idx
|
|
629
|
+
try:
|
|
630
|
+
curses.init_color(slot, *rgb[nm])
|
|
631
|
+
fg[nm] = slot
|
|
632
|
+
except curses.error:
|
|
633
|
+
pass
|
|
634
|
+
fg["red"] = curses.COLOR_RED
|
|
635
|
+
pairs = {}
|
|
636
|
+
for i, nm in enumerate(("cyan", "violet", "pink", "green", "yellow", "red"),
|
|
637
|
+
start=1):
|
|
638
|
+
try:
|
|
639
|
+
curses.init_pair(i, fg[nm], -1)
|
|
640
|
+
pairs[nm] = curses.color_pair(i)
|
|
641
|
+
except curses.error:
|
|
642
|
+
pairs[nm] = 0
|
|
643
|
+
return pairs
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def _aurora_theme(curses):
|
|
647
|
+
"""role -> curses attr. Monochrome baseline, upgraded to Aurora where able."""
|
|
648
|
+
t = {"title": curses.A_BOLD, "prompt": curses.A_BOLD, "query": curses.A_BOLD,
|
|
649
|
+
"count": curses.A_DIM, "help": curses.A_DIM, "star": curses.A_NORMAL,
|
|
650
|
+
"name": curses.A_NORMAL, "sel_bar": curses.A_REVERSE,
|
|
651
|
+
"sel_name": curses.A_BOLD, "match": curses.A_BOLD,
|
|
652
|
+
"version": curses.A_DIM, "tap": curses.A_NORMAL,
|
|
653
|
+
"rule": [curses.A_NORMAL, curses.A_NORMAL, curses.A_NORMAL],
|
|
654
|
+
"scroll": curses.A_DIM, "detail_name": curses.A_BOLD,
|
|
655
|
+
"detail_meta": curses.A_DIM, "detail_tag": curses.A_NORMAL,
|
|
656
|
+
"dot_r": curses.A_NORMAL, "dot_y": curses.A_NORMAL,
|
|
657
|
+
"dot_g": curses.A_NORMAL}
|
|
658
|
+
if not hasattr(curses, "start_color"):
|
|
659
|
+
return t
|
|
660
|
+
try:
|
|
661
|
+
curses.start_color()
|
|
662
|
+
curses.use_default_colors()
|
|
663
|
+
except curses.error:
|
|
664
|
+
return t
|
|
665
|
+
p = _aurora_pairs(curses)
|
|
666
|
+
|
|
667
|
+
def P(nm, bold=False):
|
|
668
|
+
a = p.get(nm, 0)
|
|
669
|
+
return (a | curses.A_BOLD) if bold else a
|
|
670
|
+
|
|
671
|
+
t.update({"title": P("cyan", True), "prompt": P("cyan", True),
|
|
672
|
+
"star": P("yellow"), "sel_bar": P("cyan", True),
|
|
673
|
+
"sel_name": P("cyan", True), "match": P("pink", True),
|
|
674
|
+
"tap": P("violet"), "rule": [P("cyan"), P("violet"), P("pink")],
|
|
675
|
+
"scroll": P("cyan"), "detail_name": P("cyan", True),
|
|
676
|
+
"detail_tag": P("pink"), "dot_r": P("red"), "dot_y": P("yellow"),
|
|
677
|
+
"dot_g": P("green")})
|
|
678
|
+
return t
|
|
679
|
+
|
|
680
|
+
|
|
547
681
|
def _browse_tui(curses, entries):
|
|
548
682
|
"""Run the curses UI. Returns the entry picked for install, or None."""
|
|
549
683
|
state = {"pick": None}
|
|
@@ -553,40 +687,83 @@ def _browse_tui(curses, entries):
|
|
|
553
687
|
curses.curs_set(0)
|
|
554
688
|
except curses.error:
|
|
555
689
|
pass
|
|
690
|
+
th = _aurora_theme(curses)
|
|
691
|
+
|
|
692
|
+
def put(y, x, s, attr=0): # clip-safe cell writer
|
|
693
|
+
if 0 <= y < _h and 0 <= x < _w - 1 and s:
|
|
694
|
+
try:
|
|
695
|
+
scr.addnstr(y, x, s, _w - 1 - x, attr)
|
|
696
|
+
except curses.error:
|
|
697
|
+
pass
|
|
698
|
+
|
|
556
699
|
filt, sel, detail = "", 0, False
|
|
557
700
|
while True:
|
|
558
701
|
q = filt.lower()
|
|
559
702
|
matches = [e for e in entries
|
|
560
703
|
if _subseq(q, (e["name"] + " " + e["tap"]).lower())]
|
|
561
704
|
sel = max(0, min(sel, len(matches) - 1))
|
|
562
|
-
|
|
705
|
+
_h, _w = scr.getmaxyx()
|
|
563
706
|
pane = 6 if detail and matches else 0
|
|
564
707
|
try:
|
|
565
708
|
scr.erase()
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
709
|
+
# titlebar: macOS traffic dots + wordmark + gradient rule
|
|
710
|
+
put(0, 0, "●", th["dot_r"])
|
|
711
|
+
put(0, 2, "●", th["dot_y"])
|
|
712
|
+
put(0, 4, "●", th["dot_g"])
|
|
713
|
+
title = " boost browse"
|
|
714
|
+
put(0, 6, title, th["title"])
|
|
715
|
+
rx = 6 + len(title) + 1
|
|
716
|
+
for (start, length), attr in zip(
|
|
717
|
+
_grad_segments(max(0, _w - 1 - rx)), th["rule"]):
|
|
718
|
+
put(0, rx + start, "─" * length, attr)
|
|
719
|
+
# prompt line: cyan chevron, query, cursor, right-aligned count
|
|
720
|
+
put(1, 0, "❯", th["prompt"])
|
|
721
|
+
put(1, 2, filt, th["query"])
|
|
722
|
+
put(1, 2 + len(filt), "▏", th["prompt"])
|
|
723
|
+
count = "%d/%d" % (len(matches), len(entries))
|
|
724
|
+
put(1, max(2 + len(filt) + 2, _w - 1 - len(count)), count,
|
|
725
|
+
th["count"])
|
|
726
|
+
put(2, 0, "↑↓ move ↵ detail i install q quit",
|
|
727
|
+
th["help"])
|
|
728
|
+
rows = max(1, _h - 3 - pane)
|
|
571
729
|
top = max(0, sel - rows + 1)
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
730
|
+
view = matches[top:top + rows]
|
|
731
|
+
namew = min(max((len(e["name"]) for e in view), default=4),
|
|
732
|
+
max(4, _w - 26))
|
|
733
|
+
bar = _scrollbar(len(matches), rows, top)
|
|
734
|
+
for i, e in enumerate(view):
|
|
735
|
+
y = 3 + i
|
|
736
|
+
chosen = top + i == sel
|
|
737
|
+
put(y, 0, "▎" if chosen else " ", th["sel_bar"])
|
|
738
|
+
put(y, 1, "★" if e.get("curated") else " ", th["star"])
|
|
739
|
+
nm = out.truncate(e["name"], namew)
|
|
740
|
+
put(y, 3, nm.ljust(namew),
|
|
741
|
+
th["sel_name"] if chosen else th["name"])
|
|
742
|
+
for pos in _match_positions(q, nm.lower()):
|
|
743
|
+
put(y, 3 + pos, nm[pos], th["match"])
|
|
744
|
+
vx = 3 + namew + 2
|
|
745
|
+
put(y, vx, "v" + e["version"], th["version"])
|
|
746
|
+
put(y, vx + len(e["version"]) + 3, e["tap"], th["tap"])
|
|
747
|
+
if bar:
|
|
748
|
+
bstart, blen = bar
|
|
749
|
+
put(y, _w - 2,
|
|
750
|
+
"█" if bstart <= i < bstart + blen else "│",
|
|
751
|
+
th["scroll"])
|
|
577
752
|
if pane and matches:
|
|
578
753
|
e = matches[sel]
|
|
579
|
-
y0 =
|
|
580
|
-
|
|
581
|
-
|
|
754
|
+
y0 = _h - pane
|
|
755
|
+
for (start, length), attr in zip(
|
|
756
|
+
_grad_segments(_w - 1), th["rule"]):
|
|
757
|
+
put(y0, start, "─" * length, attr)
|
|
758
|
+
tags = " ".join("#" + str(t) for t in
|
|
582
759
|
(e.get("meta", {}).get("tags") or []))
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
760
|
+
put(y0 + 1, 1, "%s v%s" % (e["name"], e["version"]),
|
|
761
|
+
th["detail_name"])
|
|
762
|
+
put(y0 + 2, 1, out.truncate(e["description"] or "", _w - 3),
|
|
763
|
+
th["name"])
|
|
764
|
+
put(y0 + 3, 1, "tap %s dir %s" % (e["tap"], e["rel_dir"]),
|
|
765
|
+
th["detail_meta"])
|
|
766
|
+
put(y0 + 4, 1, tags or "no tags", th["detail_tag"])
|
|
590
767
|
scr.refresh()
|
|
591
768
|
except curses.error:
|
|
592
769
|
pass # terminal too small mid-draw; retry on next key
|
|
@@ -116,7 +116,6 @@ docs/roadmap/items/finish-mutation-hardening-across-core.md
|
|
|
116
116
|
docs/roadmap/items/fix-self-update-version-detection-dead-branch.md
|
|
117
117
|
docs/roadmap/items/frontmatter-scalar-over-coercion.md
|
|
118
118
|
docs/roadmap/items/github-pages-deploy-is-broken-every-push.md
|
|
119
|
-
docs/roadmap/items/guard-output-table-against-ragged-rows.md
|
|
120
119
|
docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md
|
|
121
120
|
docs/roadmap/items/html-validation-html-validate.md
|
|
122
121
|
docs/roadmap/items/integrity-verification-boost-verify.md
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
<code>NO_COLOR</code> and non-TTY are always respected.
|
|
143
143
|
</p>
|
|
144
144
|
<div class="stats">
|
|
145
|
-
<div class="stat reveal"><b>
|
|
145
|
+
<div class="stat reveal"><b>26</b><span>Design opportunities</span></div>
|
|
146
146
|
<div class="stat reveal"><b>5</b><span>Design tracks</span></div>
|
|
147
147
|
<div class="stat reveal"><b>0</b><span>New dependencies</span></div>
|
|
148
148
|
<div class="stat reveal"><b>1:1</b><span>Web ↔ CLI parity goal</span></div>
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
<div class="track-icon"></div>
|
|
396
396
|
<div>
|
|
397
397
|
<h3>Command-by-command Polish</h3>
|
|
398
|
-
<p>The screens users actually stare at — front door, doctor, list, info.</p>
|
|
398
|
+
<p>The screens users actually stare at — front door, doctor, list, info, browse.</p>
|
|
399
399
|
</div>
|
|
400
400
|
<span class="n"><!-- ROADMAP:count track=commands -->6 items<!-- /ROADMAP:count --></span>
|
|
401
401
|
</div>
|
|
@@ -451,6 +451,14 @@
|
|
|
451
451
|
</article>
|
|
452
452
|
<!-- /ROADMAP:cards -->
|
|
453
453
|
|
|
454
|
+
<article class="ritem" data-track="commands" data-impact="high">
|
|
455
|
+
<div class="ritem-top"><span class="rid">BOOST-D26</span><span class="rstatus done">Shipped</span></div>
|
|
456
|
+
<h4><code>browse</code> — Aurora the full-screen TUI</h4>
|
|
457
|
+
<p>The interactive <code>browse</code> pane was the <b>last off-theme surface</b>: a curses screen drawn in raw <code>A_BOLD</code>/<code>A_DIM</code>/<code>A_REVERSE</code> — zero brand color while every other command spoke Aurora. Rebuilt it as a first-class TUI: a macOS <b>titlebar</b> (traffic-light dots + <code>boost browse</code> wordmark under a cyan→violet→pink gradient rule), an <code>❯</code> chevron prompt with a live cursor, <b>fuzzy-match highlighting</b> (matched chars glow pink as you type), a cyan selection bar, yellow curated <code>★</code>, violet tap labels, a right-edge scrollbar, and a gradient-topped detail pane. Colors come from the single-source <code>out.TOKENS</code> via <code>init_color</code> — parity-locked to <code>style/boost.css</code> — and degrade truecolor → 8-color → monochrome. Stdlib only.</p>
|
|
458
|
+
<div class="rmeta"><span class="m-cat">commands</span><span>Complexity L</span><span class="m-imp high">Impact High</span><span class="m-wow">★★★★★</span></div>
|
|
459
|
+
<div class="rref"><code>commands/discovery.py · _browse_tui / _aurora_theme</code></div>
|
|
460
|
+
</article>
|
|
461
|
+
|
|
454
462
|
</div>
|
|
455
463
|
</section>
|
|
456
464
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: fix-self-update-version-detection-dead-branch
|
|
3
|
+
board: code
|
|
4
|
+
section: shipped
|
|
5
|
+
status: shipped
|
|
6
|
+
category: Correctness
|
|
7
|
+
complexity: S
|
|
8
|
+
impact: High
|
|
9
|
+
wow: 3
|
|
10
|
+
note: broken headline command, now works
|
|
11
|
+
order: 7
|
|
12
|
+
owner: loop/self-update-fix
|
|
13
|
+
pr:
|
|
14
|
+
title: Fix <code>self-update</code> version detection (dead branch)
|
|
15
|
+
---
|
|
16
|
+
<code>boost self-update</code> greped <code>__init__.py</code> for a
|
|
17
|
+
<code>__version__ = "…"</code> literal that setuptools-scm never
|
|
18
|
+
writes, so the regex never matched and it always reported
|
|
19
|
+
<b>"already up to date"</b> — even after a pull brought a newer tag.
|
|
20
|
+
The success path was <em>unreachable</em>. It now derives the pulled
|
|
21
|
+
version straight from git (<code>describe --tags</code>, mirroring
|
|
22
|
+
<code>_detect_version()</code>) and treats a moved <code>HEAD</code>
|
|
23
|
+
as the "an update landed" signal, so the version-bump path finally
|
|
24
|
+
fires. Regression tests cover both the up-to-date and update-landed
|
|
25
|
+
branches.
|
|
@@ -115,10 +115,10 @@
|
|
|
115
115
|
<section class="block reveal">
|
|
116
116
|
<div class="stats">
|
|
117
117
|
<!-- ROADMAP:stats -->
|
|
118
|
-
<div class="stat"><b>
|
|
118
|
+
<div class="stat"><b>7</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>
|
|
121
|
+
<div class="stat"><b>83</b><span>Loop finds</span></div>
|
|
122
122
|
<!-- /ROADMAP:stats -->
|
|
123
123
|
</div>
|
|
124
124
|
</section>
|
|
@@ -227,6 +227,27 @@
|
|
|
227
227
|
<span class="m">Wow <b class="wow">★★★★</b></span>
|
|
228
228
|
</div>
|
|
229
229
|
</article>
|
|
230
|
+
|
|
231
|
+
<article class="cap rcard">
|
|
232
|
+
<div class="head"><span class="pill shipped">Shipped</span><span class="cat">Correctness</span></div>
|
|
233
|
+
<h3>Fix <code>self-update</code> version detection (dead branch)</h3>
|
|
234
|
+
<p><code>boost self-update</code> greped <code>__init__.py</code> for a
|
|
235
|
+
<code>__version__ = "…"</code> literal that setuptools-scm never
|
|
236
|
+
writes, so the regex never matched and it always reported
|
|
237
|
+
<b>"already up to date"</b> — even after a pull brought a newer tag.
|
|
238
|
+
The success path was <em>unreachable</em>. It now derives the pulled
|
|
239
|
+
version straight from git (<code>describe --tags</code>, mirroring
|
|
240
|
+
<code>_detect_version()</code>) and treats a moved <code>HEAD</code>
|
|
241
|
+
as the "an update landed" signal, so the version-bump path finally
|
|
242
|
+
fires. Regression tests cover both the up-to-date and update-landed
|
|
243
|
+
branches.</p>
|
|
244
|
+
<div class="meta">
|
|
245
|
+
<span class="m">Complexity <b>S</b></span>
|
|
246
|
+
<span class="m hi">Impact <b>High</b></span>
|
|
247
|
+
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
248
|
+
<span class="m">broken headline command, now works</span>
|
|
249
|
+
</div>
|
|
250
|
+
</article>
|
|
230
251
|
<!-- /ROADMAP:cards -->
|
|
231
252
|
|
|
232
253
|
</div>
|
|
@@ -377,17 +398,6 @@
|
|
|
377
398
|
</div>
|
|
378
399
|
</article>
|
|
379
400
|
|
|
380
|
-
<article class="cap rcard">
|
|
381
|
-
<div class="head"><span class="pill next">Next</span><span class="cat">Correctness</span></div>
|
|
382
|
-
<h3>Fix <code>self-update</code> version detection (dead branch)</h3>
|
|
383
|
-
<p><code>cmd_self_update</code> greps <code>__init__.py</code> for a <code>__version__ = "…"</code> literal (<code>commands/configuration.py:1090</code>), but the constant is <code>_detect_version()</code> (setuptools-scm — no literal). The regex never matches, so it reports <b>"already up to date"</b> even after a pull brings a newer tag. The success path is unreachable.</p>
|
|
384
|
-
<div class="meta">
|
|
385
|
-
<span class="m">Complexity <b>S</b></span>
|
|
386
|
-
<span class="m hi">Impact <b>High</b></span>
|
|
387
|
-
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
388
|
-
</div>
|
|
389
|
-
</article>
|
|
390
|
-
|
|
391
401
|
<article class="cap rcard">
|
|
392
402
|
<div class="head"><span class="pill next">Next</span><span class="cat">Correctness</span></div>
|
|
393
403
|
<h3>Consolidate skill-staleness / drift logic into core</h3>
|
|
@@ -564,17 +574,6 @@
|
|
|
564
574
|
</div>
|
|
565
575
|
</article>
|
|
566
576
|
|
|
567
|
-
<article class="cap rcard">
|
|
568
|
-
<div class="head"><span class="pill planned">Planned</span><span class="cat">Robustness</span></div>
|
|
569
|
-
<h3>Guard <code>output.table</code> against ragged rows</h3>
|
|
570
|
-
<p>Column-width computation does <code>max(… for r in rows if i < len(r))</code> (<code>core/output.py:300</code>), which raises <code>max() arg is empty</code> when a header column has no matching cell in any row. Add <code>default=0</code>.</p>
|
|
571
|
-
<div class="meta">
|
|
572
|
-
<span class="m">Complexity <b>S</b></span>
|
|
573
|
-
<span class="m">Impact <b>Low</b></span>
|
|
574
|
-
<span class="m">Wow <b class="wow">★</b></span>
|
|
575
|
-
</div>
|
|
576
|
-
</article>
|
|
577
|
-
|
|
578
577
|
<article class="cap rcard">
|
|
579
578
|
<div class="head"><span class="pill planned">Planned</span><span class="cat">Tech-debt</span></div>
|
|
580
579
|
<h3>Reuse helpers; kill minor dead work</h3>
|
{boost_skill_cli-1.0.67 → boost_skill_cli-1.0.69}/tests/functional/test_cli_configuration.py
RENAMED
|
@@ -809,20 +809,54 @@ class TestSelfUpdate:
|
|
|
809
809
|
assert "boost is not running from a git checkout" in r.err
|
|
810
810
|
assert "git clone the boost repo" in r.err
|
|
811
811
|
|
|
812
|
-
def
|
|
812
|
+
def _fake_git(self, monkeypatch, *, before, after, described):
|
|
813
|
+
"""Stub gitutil so self-update sees a controlled HEAD move + describe.
|
|
814
|
+
|
|
815
|
+
``rev-parse HEAD`` returns ``before`` until the pull runs, ``after``
|
|
816
|
+
afterwards; ``describe`` returns ``described``; ``pull`` is a no-op.
|
|
817
|
+
"""
|
|
813
818
|
calls = []
|
|
819
|
+
pulled = {"done": False}
|
|
814
820
|
monkeypatch.setattr("boost_cli.core.gitutil.is_repo", lambda p: True)
|
|
815
821
|
|
|
816
822
|
def fake_run(args, **kw):
|
|
817
823
|
calls.append(list(args))
|
|
824
|
+
if "pull" in args:
|
|
825
|
+
pulled["done"] = True
|
|
826
|
+
return _proc(args, 0)
|
|
827
|
+
if "rev-parse" in args:
|
|
828
|
+
return _proc(args, 0, out=(after if pulled["done"] else before))
|
|
829
|
+
if "describe" in args:
|
|
830
|
+
return _proc(args, 0, out=described)
|
|
818
831
|
return _proc(args, 0)
|
|
819
832
|
|
|
820
833
|
monkeypatch.setattr("boost_cli.core.gitutil.run", fake_run)
|
|
834
|
+
return calls
|
|
835
|
+
|
|
836
|
+
def test_reports_already_up_to_date_when_head_unchanged(
|
|
837
|
+
self, boost, sandbox, monkeypatch):
|
|
838
|
+
# HEAD is identical before and after the pull -> nothing landed
|
|
839
|
+
calls = self._fake_git(monkeypatch, before="sha1\n", after="sha1\n",
|
|
840
|
+
described="v2.3.3\n")
|
|
821
841
|
r = boost("self-update")
|
|
822
842
|
root = paths.repo_root()
|
|
823
|
-
assert calls == [["-C", str(root), "pull", "--ff-only"]]
|
|
824
843
|
from boost_cli import __version__
|
|
844
|
+
assert ["-C", str(root), "pull", "--ff-only"] in calls
|
|
825
845
|
assert ("already up to date (v%s)" % __version__) in r.out
|
|
846
|
+
assert "→" not in r.out
|
|
847
|
+
ev = journal.events(action="self-update")[0]
|
|
848
|
+
assert ev["subject"] == __version__ and ev["previous"] == __version__
|
|
849
|
+
|
|
850
|
+
def test_reports_new_version_when_pull_advances_head(
|
|
851
|
+
self, boost, sandbox, monkeypatch):
|
|
852
|
+
# the pull fast-forwards HEAD -> report the git-described new version.
|
|
853
|
+
# This path was unreachable before the fix (the old code grepped
|
|
854
|
+
# __init__.py for a __version__ literal setuptools-scm never writes).
|
|
855
|
+
self._fake_git(monkeypatch, before="oldsha\n", after="newsha\n",
|
|
856
|
+
described="v9.9.9\n")
|
|
857
|
+
r = boost("self-update")
|
|
858
|
+
from boost_cli import __version__
|
|
859
|
+
assert ("boost v%s → v9.9.9" % __version__) in r.out
|
|
826
860
|
ev = journal.events(action="self-update")[0]
|
|
827
|
-
assert ev["subject"] ==
|
|
861
|
+
assert ev["subject"] == "9.9.9" # git-derived, not the stale const
|
|
828
862
|
assert ev["previous"] == __version__
|
|
@@ -627,11 +627,117 @@ class TestBrowse:
|
|
|
627
627
|
FakeCurses.KEY_BACKSPACE, ord("i")])
|
|
628
628
|
picked = discovery._browse_tui(fake, entries)
|
|
629
629
|
assert picked["name"] == "brainstorming"
|
|
630
|
-
|
|
630
|
+
# the typed query is echoed after the "❯" prompt
|
|
631
|
+
assert "t" in fake.drawn
|
|
632
|
+
assert any("boost browse" in s for s in fake.drawn)
|
|
631
633
|
# ESC quits without a pick
|
|
632
634
|
assert discovery._browse_tui(FakeCurses([27]), entries) is None
|
|
633
635
|
|
|
634
636
|
|
|
637
|
+
class TestBrowseAurora:
|
|
638
|
+
"""The curses browser paints itself in the single-source Aurora palette."""
|
|
639
|
+
|
|
640
|
+
def test_curses_rgb1000_parity_with_tokens(self):
|
|
641
|
+
from boost_cli.commands import discovery
|
|
642
|
+
from boost_cli.core import output as out
|
|
643
|
+
rgb = discovery._curses_rgb1000()
|
|
644
|
+
assert set(rgb) == set(out.TOKENS)
|
|
645
|
+
# cyan #22d3ee scaled 0..255 -> 0..1000
|
|
646
|
+
r, g, b = out.TOKENS["cyan"]
|
|
647
|
+
assert rgb["cyan"] == (round(r / 255 * 1000), round(g / 255 * 1000),
|
|
648
|
+
round(b / 255 * 1000))
|
|
649
|
+
for triple in rgb.values():
|
|
650
|
+
assert all(0 <= c <= 1000 for c in triple)
|
|
651
|
+
|
|
652
|
+
def test_nearest_base_covers_every_token(self):
|
|
653
|
+
import curses
|
|
654
|
+
from boost_cli.commands import discovery
|
|
655
|
+
from boost_cli.core import output as out
|
|
656
|
+
base = discovery._nearest_base(curses)
|
|
657
|
+
assert set(base) == set(out.TOKENS)
|
|
658
|
+
assert base["cyan"] == curses.COLOR_CYAN
|
|
659
|
+
assert base["violet"] == base["pink"] == curses.COLOR_MAGENTA
|
|
660
|
+
|
|
661
|
+
def test_match_positions(self):
|
|
662
|
+
from boost_cli.commands import discovery
|
|
663
|
+
assert discovery._match_positions("bs", "brainstorm") == [0, 5]
|
|
664
|
+
assert discovery._match_positions("", "anything") == []
|
|
665
|
+
assert discovery._match_positions("zzz", "brainstorm") == []
|
|
666
|
+
# left-to-right greedy: first 'o', then trailing 'm'
|
|
667
|
+
assert discovery._match_positions("om", "brainstorm") == [7, 9]
|
|
668
|
+
|
|
669
|
+
def test_scrollbar_math(self):
|
|
670
|
+
from boost_cli.commands import discovery
|
|
671
|
+
assert discovery._scrollbar(5, 10, 0) is None # all fits
|
|
672
|
+
assert discovery._scrollbar(10, 0, 0) is None # no rows
|
|
673
|
+
start, length = discovery._scrollbar(100, 10, 0)
|
|
674
|
+
assert start == 0 and 1 <= length <= 10
|
|
675
|
+
end_start, _ = discovery._scrollbar(100, 10, 90) # scrolled to bottom
|
|
676
|
+
assert end_start == 10 - length
|
|
677
|
+
|
|
678
|
+
def test_grad_segments_partition(self):
|
|
679
|
+
from boost_cli.commands import discovery
|
|
680
|
+
assert discovery._grad_segments(0) == []
|
|
681
|
+
segs = discovery._grad_segments(10, 3)
|
|
682
|
+
assert [s[1] for s in segs] == [4, 3, 3] # sums to width
|
|
683
|
+
assert sum(s[1] for s in segs) == 10
|
|
684
|
+
assert [s[0] for s in segs] == [0, 4, 7] # contiguous
|
|
685
|
+
assert len(discovery._grad_segments(2, 3)) == 2 # n clamps to width
|
|
686
|
+
|
|
687
|
+
def test_theme_upgrades_to_colour(self):
|
|
688
|
+
from boost_cli.commands import discovery
|
|
689
|
+
|
|
690
|
+
class ColorCurses:
|
|
691
|
+
COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN = 6, 5, 2
|
|
692
|
+
COLOR_YELLOW, COLOR_RED = 3, 1
|
|
693
|
+
COLORS = 256
|
|
694
|
+
A_BOLD, A_DIM, A_REVERSE, A_NORMAL = 1, 2, 4, 0
|
|
695
|
+
|
|
696
|
+
class error(Exception):
|
|
697
|
+
pass
|
|
698
|
+
|
|
699
|
+
def __init__(self):
|
|
700
|
+
self.colors, self.pairs = {}, {}
|
|
701
|
+
|
|
702
|
+
def start_color(self):
|
|
703
|
+
pass
|
|
704
|
+
|
|
705
|
+
def use_default_colors(self):
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
def can_change_color(self):
|
|
709
|
+
return True
|
|
710
|
+
|
|
711
|
+
def init_color(self, slot, r, g, b):
|
|
712
|
+
self.colors[slot] = (r, g, b)
|
|
713
|
+
|
|
714
|
+
def init_pair(self, i, fg, bg):
|
|
715
|
+
self.pairs[i] = (fg, bg)
|
|
716
|
+
|
|
717
|
+
def color_pair(self, i):
|
|
718
|
+
return i << 8
|
|
719
|
+
|
|
720
|
+
cc = ColorCurses()
|
|
721
|
+
th = discovery._aurora_theme(cc)
|
|
722
|
+
# custom colours were defined from the parity-locked palette
|
|
723
|
+
assert cc.colors[16] == discovery._curses_rgb1000()["cyan"]
|
|
724
|
+
# title carries a colour pair plus bold, not bare monochrome
|
|
725
|
+
assert th["title"] & cc.A_BOLD
|
|
726
|
+
assert th["title"] & ~cc.A_BOLD
|
|
727
|
+
assert len(th["rule"]) == 3
|
|
728
|
+
assert th["rule"][0] != th["rule"][1] != th["rule"][2]
|
|
729
|
+
|
|
730
|
+
def test_theme_falls_back_without_colour(self):
|
|
731
|
+
from boost_cli.commands import discovery
|
|
732
|
+
|
|
733
|
+
class MonoCurses:
|
|
734
|
+
A_BOLD, A_DIM, A_REVERSE, A_NORMAL = 1, 2, 4, 0
|
|
735
|
+
th = discovery._aurora_theme(MonoCurses)
|
|
736
|
+
assert th["title"] == MonoCurses.A_BOLD
|
|
737
|
+
assert th["sel_bar"] == MonoCurses.A_REVERSE
|
|
738
|
+
assert th["rule"] == [MonoCurses.A_NORMAL] * 3
|
|
739
|
+
|
|
740
|
+
|
|
635
741
|
# ---------------------------------------------------------------- trending
|
|
636
742
|
|
|
637
743
|
class TestTrending:
|