boost-skill-cli 1.0.68__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.68 → boost_skill_cli-1.0.69}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/discovery.py +198 -21
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/SOURCES.txt +0 -1
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/design-roadmap.html +10 -2
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap.html +1 -12
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_discovery.py +107 -1
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_output.py +15 -0
- boost_skill_cli-1.0.68/docs/roadmap/items/guard-output-table-against-ragged-rows.md +0 -16
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/.gitattributes +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/.gitignore +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/LICENSE +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/Makefile +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/README.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/index.html +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.68 → 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.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/index.html +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/setup.cfg +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/style/README.md +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/style/boost.css +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/style/boost.js +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/style/demo.html +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.68 → 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
|
|
@@ -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
|
|
|
@@ -118,7 +118,7 @@
|
|
|
118
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>
|
|
@@ -574,17 +574,6 @@
|
|
|
574
574
|
</div>
|
|
575
575
|
</article>
|
|
576
576
|
|
|
577
|
-
<article class="cap rcard">
|
|
578
|
-
<div class="head"><span class="pill planned">Planned</span><span class="cat">Robustness</span></div>
|
|
579
|
-
<h3>Guard <code>output.table</code> against ragged rows</h3>
|
|
580
|
-
<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>
|
|
581
|
-
<div class="meta">
|
|
582
|
-
<span class="m">Complexity <b>S</b></span>
|
|
583
|
-
<span class="m">Impact <b>Low</b></span>
|
|
584
|
-
<span class="m">Wow <b class="wow">★</b></span>
|
|
585
|
-
</div>
|
|
586
|
-
</article>
|
|
587
|
-
|
|
588
577
|
<article class="cap rcard">
|
|
589
578
|
<div class="head"><span class="pill planned">Planned</span><span class="cat">Tech-debt</span></div>
|
|
590
579
|
<h3>Reuse helpers; kill minor dead work</h3>
|
|
@@ -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:
|
|
@@ -584,6 +584,21 @@ class TestTable:
|
|
|
584
584
|
output.table([("a",), ("bb", "c")])
|
|
585
585
|
assert capsys.readouterr().out == "a\nbb c\n"
|
|
586
586
|
|
|
587
|
+
def test_header_arity_exceeds_every_row(self, capsys):
|
|
588
|
+
# a header column with no data cell in any row must not raise: the
|
|
589
|
+
# width generator is guarded by `i < len(r)`, and the header row itself
|
|
590
|
+
# (part of all_rows) always supplies its own columns.
|
|
591
|
+
output.table([("1",)], headers=["A", "B", "C"])
|
|
592
|
+
assert capsys.readouterr().out == "A B C\n1\n"
|
|
593
|
+
|
|
594
|
+
def test_degenerate_row_shapes_never_raise(self, capsys):
|
|
595
|
+
# empty tuples and all-empty rows are the shapes the (non-)bug feared;
|
|
596
|
+
# assert they render without a `max() arg is empty` crash.
|
|
597
|
+
for rows in ([(), ("a",)], [(), ()], [("a", "b"), ()]):
|
|
598
|
+
output.table(rows) # must not raise
|
|
599
|
+
output.table([(), ()], headers=["A", "B"])
|
|
600
|
+
assert "A B" in capsys.readouterr().out
|
|
601
|
+
|
|
587
602
|
def test_non_string_cells_coerced(self, capsys):
|
|
588
603
|
output.table([(1, 22.5)])
|
|
589
604
|
assert capsys.readouterr().out == "1 22.5\n"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: guard-output-table-against-ragged-rows
|
|
3
|
-
board: code
|
|
4
|
-
section: internals
|
|
5
|
-
status: planned
|
|
6
|
-
category: Robustness
|
|
7
|
-
complexity: S
|
|
8
|
-
impact: Low
|
|
9
|
-
wow: 1
|
|
10
|
-
note:
|
|
11
|
-
order: 19
|
|
12
|
-
owner:
|
|
13
|
-
pr:
|
|
14
|
-
title: Guard <code>output.table</code> against ragged rows
|
|
15
|
-
---
|
|
16
|
-
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>.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boost_skill_cli-1.0.68 → boost_skill_cli-1.0.69}/boost_skill_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|