boost-skill-cli 1.0.33__tar.gz → 1.0.35__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/bmad.py +2 -2
  4. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/configuration.py +11 -11
  5. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/discovery.py +10 -9
  6. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/hooks.py +2 -2
  7. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/info.py +11 -11
  8. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/intelligence.py +9 -9
  9. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/quality.py +15 -15
  10. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/taps.py +5 -4
  11. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/team.py +7 -7
  12. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/output.py +20 -10
  13. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  14. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/design-roadmap.html +5 -5
  15. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_output.py +23 -0
  16. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/.gitignore +0 -0
  17. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/CLAUDE.md +0 -0
  18. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/CONTRIBUTING.md +0 -0
  19. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/LICENSE +0 -0
  20. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/MANIFEST.in +0 -0
  21. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/Makefile +0 -0
  22. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/README.md +0 -0
  23. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/SECURITY.md +0 -0
  24. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost +0 -0
  25. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/__init__.py +0 -0
  26. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/__main__.py +0 -0
  27. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/cli.py +0 -0
  28. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/cliparse.py +0 -0
  29. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/__init__.py +0 -0
  30. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/commands/pkg.py +0 -0
  31. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/__init__.py +0 -0
  32. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/agents.py +0 -0
  33. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/ai.py +0 -0
  34. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/catalog.py +0 -0
  35. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/claude_settings.py +0 -0
  36. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/config.py +0 -0
  37. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/dense.py +0 -0
  38. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/embed.py +0 -0
  39. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/frontmatter.py +0 -0
  40. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/gitutil.py +0 -0
  41. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/journal.py +0 -0
  42. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/lockfile.py +0 -0
  43. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/logs.py +0 -0
  44. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/paths.py +0 -0
  45. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/policy.py +0 -0
  46. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/rag.py +0 -0
  47. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/registry.py +0 -0
  48. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/store.py +0 -0
  49. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/core/util.py +0 -0
  50. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/data/registries.json +0 -0
  51. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_cli/errors.py +0 -0
  52. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
  53. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  54. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  55. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/requires.txt +0 -0
  56. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/boost_skill_cli.egg-info/top_level.txt +0 -0
  57. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/.claude/settings.local.json +0 -0
  58. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/DEBUGGING.md +0 -0
  59. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/demo.gif +0 -0
  60. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/demo.tape +0 -0
  61. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/index.html +0 -0
  62. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/rag-architecture.md +0 -0
  63. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/docs/roadmap.html +0 -0
  64. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/pyproject.toml +0 -0
  65. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/scripts/build_registries.py +0 -0
  66. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/scripts/mutation_gate.py +0 -0
  67. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/setup.cfg +0 -0
  68. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/style/README.md +0 -0
  69. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/style/boost.css +0 -0
  70. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/style/boost.js +0 -0
  71. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/style/demo.html +0 -0
  72. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/conftest.py +0 -0
  73. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_bmad.py +0 -0
  74. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_configuration.py +0 -0
  75. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_discovery.py +0 -0
  76. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_hooks.py +0 -0
  77. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_info.py +0 -0
  78. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_intelligence.py +0 -0
  79. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_pkg.py +0 -0
  80. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_quality.py +0 -0
  81. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_taps.py +0 -0
  82. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_cli_team.py +0 -0
  83. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/functional/test_everyday_loop.py +0 -0
  84. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/make_fixture.py +0 -0
  85. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/smoke.sh +0 -0
  86. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_agents.py +0 -0
  87. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_ai.py +0 -0
  88. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_catalog.py +0 -0
  89. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_claude_settings.py +0 -0
  90. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_cliparse.py +0 -0
  91. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_config.py +0 -0
  92. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_dense.py +0 -0
  93. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_embed.py +0 -0
  94. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_errors_and_cli_table.py +0 -0
  95. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_frontmatter.py +0 -0
  96. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_gitutil.py +0 -0
  97. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_journal.py +0 -0
  98. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_lockfile.py +0 -0
  99. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_logs.py +0 -0
  100. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_mutation_hardening.py +0 -0
  101. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_paths.py +0 -0
  102. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_policy.py +0 -0
  103. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_rag.py +0 -0
  104. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_registry.py +0 -0
  105. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_store.py +0 -0
  106. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_util.py +0 -0
  107. {boost_skill_cli-1.0.33 → boost_skill_cli-1.0.35}/tests/unit/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.33
3
+ Version: 1.0.35
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.33'
22
- __version_tuple__ = version_tuple = (1, 0, 33)
21
+ __version__ = version = '1.0.35'
22
+ __version_tuple__ = version_tuple = (1, 0, 35)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -18,7 +18,6 @@ runtime is per-project (`boost bmad init`).
18
18
  """
19
19
  from __future__ import annotations
20
20
 
21
- import argparse
22
21
  import getpass
23
22
  import json
24
23
  import os
@@ -29,6 +28,7 @@ import subprocess
29
28
  import tempfile
30
29
  from pathlib import Path
31
30
 
31
+ from .. import cliparse
32
32
  from ..core import claude_settings as cs
33
33
  from ..core import journal
34
34
  from ..core import output as out
@@ -64,7 +64,7 @@ _ACTIONS = ("install", "init", "startup", "orient", "uninstall",
64
64
 
65
65
 
66
66
  def cmd_bmad(argv) -> int:
67
- p = argparse.ArgumentParser(
67
+ p = cliparse.parser(
68
68
  prog="boost bmad",
69
69
  description="Install & manage the BMAD Method (scope-aware, toggleable)")
70
70
  p.add_argument("action", choices=_ACTIONS, help=" | ".join(_ACTIONS))
@@ -2,7 +2,6 @@
2
2
  completions, schedule, serve, mcp, self-update."""
3
3
  from __future__ import annotations
4
4
 
5
- import argparse
6
5
  import errno
7
6
  import getpass
8
7
  import html
@@ -18,6 +17,7 @@ from datetime import datetime, timedelta
18
17
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
19
18
  from pathlib import Path
20
19
 
20
+ from .. import cliparse
21
21
  from .. import __version__
22
22
  from ..core import agents, catalog, config, frontmatter, gitutil, journal
23
23
  from ..core import lockfile, paths, policy, rag, registry, store, util
@@ -47,7 +47,7 @@ def _user() -> str:
47
47
 
48
48
  def cmd_config(argv) -> int:
49
49
  """boost config [list|get KEY|set KEY VALUE|unset KEY] [--json]"""
50
- p = argparse.ArgumentParser(
50
+ p = cliparse.parser(
51
51
  prog="boost config",
52
52
  description="Display or modify boost configuration")
53
53
  p.add_argument("action", nargs="?", default="list",
@@ -112,7 +112,7 @@ def cmd_config(argv) -> int:
112
112
 
113
113
  def cmd_clean(argv) -> int:
114
114
  """boost clean [--dry-run] [--deep]"""
115
- p = argparse.ArgumentParser(
115
+ p = cliparse.parser(
116
116
  prog="boost clean",
117
117
  description="Clear stale caches & broken symlinks")
118
118
  p.add_argument("--dry-run", action="store_true",
@@ -215,7 +215,7 @@ TODO: a concrete input/output example
215
215
 
216
216
  def cmd_create(argv) -> int:
217
217
  """boost create NAME [--description D] [--dir DIR] [--install]"""
218
- p = argparse.ArgumentParser(
218
+ p = cliparse.parser(
219
219
  prog="boost create",
220
220
  description="Scaffold a new skill from a template")
221
221
  p.add_argument("name", help="skill name (slugified)")
@@ -271,7 +271,7 @@ def _parse_policy_value(key: str, raw: str):
271
271
 
272
272
  def cmd_policy(argv) -> int:
273
273
  """boost policy [list|set KEY VALUE|unset KEY|check] [--json]"""
274
- p = argparse.ArgumentParser(
274
+ p = cliparse.parser(
275
275
  prog="boost policy",
276
276
  description="Manage & enforce skill governance policies")
277
277
  p.add_argument("action", nargs="?", default="list",
@@ -399,7 +399,7 @@ jobs:
399
399
 
400
400
  def cmd_onboard(argv) -> int:
401
401
  """boost onboard [--repo DIR] [--pr] [--dry-run]"""
402
- p = argparse.ArgumentParser(
402
+ p = cliparse.parser(
403
403
  prog="boost onboard",
404
404
  description="Add skill-tracker telemetry to a repo & open a PR")
405
405
  p.add_argument("--repo", default=".", help="repository directory (default: .)")
@@ -481,7 +481,7 @@ def _sq(s: str) -> str:
481
481
 
482
482
  def cmd_completions(argv) -> int:
483
483
  """boost completions [bash|zsh|fish]"""
484
- p = argparse.ArgumentParser(
484
+ p = cliparse.parser(
485
485
  prog="boost completions",
486
486
  description="Generate shell tab-completion scripts")
487
487
  p.add_argument("shell", nargs="?", choices=("bash", "zsh", "fish"),
@@ -597,7 +597,7 @@ def _interval_label(seconds) -> str:
597
597
 
598
598
  def cmd_schedule(argv) -> int:
599
599
  """boost schedule [status|enable [--interval 6h|12h|daily]|disable]"""
600
- p = argparse.ArgumentParser(
600
+ p = cliparse.parser(
601
601
  prog="boost schedule",
602
602
  description="Manage automatic skill-sync scheduling")
603
603
  p.add_argument("action", nargs="?", default="status",
@@ -832,7 +832,7 @@ class _CatalogHandler(BaseHTTPRequestHandler):
832
832
 
833
833
  def cmd_serve(argv) -> int:
834
834
  """boost serve [--port N] [--host H]"""
835
- p = argparse.ArgumentParser(
835
+ p = cliparse.parser(
836
836
  prog="boost serve",
837
837
  description="Serve the skill catalog over HTTP (port 8787)")
838
838
  p.add_argument("--port", type=int,
@@ -1029,7 +1029,7 @@ def _mcp_serve_stdio() -> int:
1029
1029
 
1030
1030
  def cmd_mcp(argv) -> int:
1031
1031
  """boost mcp [register|unregister] [--stdio]"""
1032
- p = argparse.ArgumentParser(
1032
+ p = cliparse.parser(
1033
1033
  prog="boost mcp",
1034
1034
  description="Register boost as an MCP server for Claude Code")
1035
1035
  p.add_argument("action", nargs="?", default="register",
@@ -1075,7 +1075,7 @@ def cmd_mcp(argv) -> int:
1075
1075
 
1076
1076
  def cmd_self_update(argv) -> int:
1077
1077
  """boost self-update"""
1078
- p = argparse.ArgumentParser(
1078
+ p = cliparse.parser(
1079
1079
  prog="boost self-update",
1080
1080
  description="Update boost itself to the latest version")
1081
1081
  p.parse_args(argv)
@@ -15,6 +15,7 @@ import sys
15
15
  import time
16
16
  from pathlib import Path
17
17
 
18
+ from .. import cliparse
18
19
  from ..core import agents, ai, catalog, dense, embed, gitutil, journal, lockfile, paths, rag, registry, store, util
19
20
  from ..core import output as out
20
21
  from ..errors import BoostError
@@ -169,7 +170,7 @@ def _ai_rank(query: str, scored):
169
170
 
170
171
  def cmd_search(argv):
171
172
  """Search the tap catalogs, optionally AI-reranked with --smart."""
172
- p = argparse.ArgumentParser(
173
+ p = cliparse.parser(
173
174
  prog="boost search",
174
175
  description="Search skills across tap registries (AI-ranked)")
175
176
  p.add_argument("query", nargs="+", help="search terms")
@@ -233,7 +234,7 @@ def cmd_search(argv):
233
234
 
234
235
  def cmd_reindex(argv):
235
236
  """Build/refresh the full-content (RAG) search index over tapped items."""
236
- p = argparse.ArgumentParser(
237
+ p = cliparse.parser(
237
238
  prog="boost reindex",
238
239
  description="Build or refresh the full-content search index")
239
240
  p.add_argument("--force", action="store_true",
@@ -282,7 +283,7 @@ def _reindex_dense(force):
282
283
 
283
284
  def cmd_index(argv):
284
285
  """Build ~/.boost/cache/discovery.json via `gh api` code search."""
285
- p = argparse.ArgumentParser(
286
+ p = cliparse.parser(
286
287
  prog="boost index",
287
288
  description="Build the discovery registry via GitHub Code Search")
288
289
  p.add_argument("--limit", type=_positive_int, default=300,
@@ -345,7 +346,7 @@ def cmd_index(argv):
345
346
 
346
347
  def cmd_discover(argv):
347
348
  """Browse/filter the GitHub-wide index built by `boost index`."""
348
- p = argparse.ArgumentParser(
349
+ p = cliparse.parser(
349
350
  prog="boost discover",
350
351
  description="Browse & search the GitHub-wide skill discovery index")
351
352
  p.add_argument("query", nargs="*", help="filter terms (repo/path substring)")
@@ -414,7 +415,7 @@ def _ai_picks(stack: dict, cands):
414
415
 
415
416
  def cmd_recommend(argv):
416
417
  """Match the detected tech stack against the catalog."""
417
- p = argparse.ArgumentParser(
418
+ p = cliparse.parser(
418
419
  prog="boost recommend",
419
420
  description="Suggest skills based on your project's tech stack")
420
421
  p.add_argument("--path", default=".", help="project directory (default: cwd)")
@@ -564,7 +565,7 @@ def _browse_tui(curses, entries):
564
565
 
565
566
  def cmd_browse(argv):
566
567
  """Full-screen fuzzy browser over every catalog entry."""
567
- p = argparse.ArgumentParser(
568
+ p = cliparse.parser(
568
569
  prog="boost browse",
569
570
  description="Interactive full-screen TUI with fuzzy search")
570
571
  p.parse_args(argv)
@@ -594,7 +595,7 @@ def cmd_browse(argv):
594
595
 
595
596
  def cmd_trending(argv):
596
597
  """Rank skills by local install events from the journal."""
597
- p = argparse.ArgumentParser(
598
+ p = cliparse.parser(
598
599
  prog="boost trending",
599
600
  description="Show trending skills by install count")
600
601
  p.add_argument("--limit", type=_positive_int, default=10,
@@ -630,7 +631,7 @@ def cmd_trending(argv):
630
631
 
631
632
  def cmd_stats(argv):
632
633
  """Lock-file, journal, and upstream stats for one skill."""
633
- p = argparse.ArgumentParser(
634
+ p = cliparse.parser(
634
635
  prog="boost stats",
635
636
  description="Install statistics & trend for a single skill")
636
637
  p.add_argument("name", help="skill name")
@@ -696,7 +697,7 @@ def cmd_stats(argv):
696
697
 
697
698
  def cmd_count(argv):
698
699
  """One-line inventory: installed / available / taps / discovery index."""
699
- p = argparse.ArgumentParser(
700
+ p = cliparse.parser(
700
701
  prog="boost count",
701
702
  description="Quick summary of installed / available / taps")
702
703
  p.add_argument("--json", action="store_true", dest="as_json",
@@ -10,8 +10,8 @@ are left untouched. See core/claude_settings.py.
10
10
  """
11
11
  from __future__ import annotations
12
12
 
13
- import argparse
14
13
 
14
+ from .. import cliparse
15
15
  from ..core import claude_settings as cs
16
16
  from ..core import journal
17
17
  from ..core import output as out
@@ -19,7 +19,7 @@ from ..errors import BoostError
19
19
 
20
20
 
21
21
  def cmd_hooks(argv) -> int:
22
- p = argparse.ArgumentParser(
22
+ p = cliparse.parser(
23
23
  prog="boost hooks",
24
24
  description="Manage Claude Code hooks (scope-aware) in settings.json")
25
25
  p.add_argument("action", choices=("add", "remove", "list"),
@@ -5,7 +5,6 @@ Read-mostly views over the lock file, the canonical store, and tap catalogs.
5
5
  """
6
6
  from __future__ import annotations
7
7
 
8
- import argparse
9
8
  import json
10
9
  import os
11
10
  import re
@@ -16,6 +15,7 @@ import textwrap
16
15
  import webbrowser
17
16
  from pathlib import Path
18
17
 
18
+ from .. import cliparse
19
19
  from ..core import ai, catalog, frontmatter, gitutil, journal, lockfile, logs, paths, registry, store, util
20
20
  from ..core import output as out
21
21
  from ..errors import BoostError
@@ -101,7 +101,7 @@ def _print_wrapped(text: str) -> None:
101
101
  # ---------------------------------------------------------------- commands
102
102
 
103
103
  def cmd_list(argv):
104
- ap = argparse.ArgumentParser(prog="boost list",
104
+ ap = cliparse.parser(prog="boost list",
105
105
  description="List installed skills")
106
106
  ap.add_argument("--tag", help="only show skills carrying this tag")
107
107
  ap.add_argument("--json", action="store_true", help="machine-readable output")
@@ -136,7 +136,7 @@ def cmd_list(argv):
136
136
 
137
137
 
138
138
  def cmd_info(argv):
139
- ap = argparse.ArgumentParser(prog="boost info",
139
+ ap = cliparse.parser(prog="boost info",
140
140
  description="Show detailed info about a skill")
141
141
  ap.add_argument("name")
142
142
  ap.add_argument("--json", action="store_true", help="machine-readable output")
@@ -242,7 +242,7 @@ def cmd_info(argv):
242
242
 
243
243
 
244
244
  def cmd_cat(argv):
245
- ap = argparse.ArgumentParser(prog="boost cat",
245
+ ap = cliparse.parser(prog="boost cat",
246
246
  description="Print a skill or rule's contents")
247
247
  ap.add_argument("name")
248
248
  ap.add_argument("--raw", action="store_true", help="no styling even on a TTY")
@@ -265,7 +265,7 @@ def cmd_cat(argv):
265
265
 
266
266
 
267
267
  def cmd_edit(argv):
268
- ap = argparse.ArgumentParser(prog="boost edit",
268
+ ap = cliparse.parser(prog="boost edit",
269
269
  description="Open a skill's SKILL.md in your editor")
270
270
  ap.add_argument("name")
271
271
  args = ap.parse_args(argv)
@@ -341,7 +341,7 @@ def _render_markdown(body: str) -> None:
341
341
 
342
342
 
343
343
  def cmd_preview(argv):
344
- ap = argparse.ArgumentParser(prog="boost preview",
344
+ ap = cliparse.parser(prog="boost preview",
345
345
  description="Render a SKILL.md with rich formatting")
346
346
  ap.add_argument("name")
347
347
  args = ap.parse_args(argv)
@@ -356,7 +356,7 @@ def cmd_preview(argv):
356
356
 
357
357
 
358
358
  def cmd_explain(argv):
359
- ap = argparse.ArgumentParser(prog="boost explain",
359
+ ap = cliparse.parser(prog="boost explain",
360
360
  description="Explain what a skill does in plain English")
361
361
  ap.add_argument("name")
362
362
  args = ap.parse_args(argv)
@@ -432,7 +432,7 @@ def _show_crashes(limit):
432
432
 
433
433
 
434
434
  def cmd_log(argv):
435
- ap = argparse.ArgumentParser(prog="boost log",
435
+ ap = cliparse.parser(prog="boost log",
436
436
  description="Git log for a skill, or boost's activity log")
437
437
  ap.add_argument("name", nargs="?", help="skill to show upstream history for")
438
438
  ap.add_argument("-n", "--limit", type=int, default=20, metavar="N",
@@ -487,7 +487,7 @@ def cmd_log(argv):
487
487
 
488
488
 
489
489
  def cmd_home(argv):
490
- ap = argparse.ArgumentParser(prog="boost home",
490
+ ap = cliparse.parser(prog="boost home",
491
491
  description="Open a skill's GitHub page in the browser")
492
492
  ap.add_argument("name")
493
493
  ap.add_argument("--print", dest="print_only", action="store_true",
@@ -517,7 +517,7 @@ def cmd_home(argv):
517
517
 
518
518
 
519
519
  def cmd_deps(argv):
520
- ap = argparse.ArgumentParser(prog="boost deps",
520
+ ap = cliparse.parser(prog="boost deps",
521
521
  description="Show dependency & conflict relationships")
522
522
  ap.add_argument("name", nargs="?", help="skill to inspect (default: check all installed)")
523
523
  ap.add_argument("--json", action="store_true", help="machine-readable output")
@@ -587,7 +587,7 @@ def cmd_deps(argv):
587
587
 
588
588
 
589
589
  def cmd_tag(argv):
590
- ap = argparse.ArgumentParser(prog="boost tag",
590
+ ap = cliparse.parser(prog="boost tag",
591
591
  description="Custom labels for organizing skills")
592
592
  ap.add_argument("--list", dest="list_all", action="store_true",
593
593
  help="show every tag and the skills carrying it")
@@ -7,7 +7,6 @@ qualitatively better. Session state lives in ~/.boost/state/.
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
- import argparse
11
10
  import difflib
12
11
  import fnmatch
13
12
  import json
@@ -18,6 +17,7 @@ from collections import Counter
18
17
  from pathlib import Path
19
18
  from typing import List, Optional, Tuple
20
19
 
20
+ from .. import cliparse
21
21
  from ..core import ai, catalog, frontmatter, gitutil, journal, lockfile, paths, store, util
22
22
  from ..core import output as out
23
23
  from ..errors import BoostError
@@ -109,7 +109,7 @@ def _current_branch(cwd: Optional[Path] = None) -> Optional[str]:
109
109
  # ---------------------------------------------------------------- distill
110
110
 
111
111
  def cmd_distill(argv: List[str]) -> int:
112
- ap = argparse.ArgumentParser(
112
+ ap = cliparse.parser(
113
113
  prog="boost distill",
114
114
  description="Merge multiple skills into one deduplicated skill")
115
115
  ap.add_argument("names", nargs="+", metavar="NAME",
@@ -207,7 +207,7 @@ _LIST_RE = re.compile(r"^([-*+]|\d+[.)])\s+")
207
207
 
208
208
 
209
209
  def cmd_simulate(argv: List[str]) -> int:
210
- ap = argparse.ArgumentParser(
210
+ ap = cliparse.parser(
211
211
  prog="boost simulate",
212
212
  description="Preview how a skill would change Claude's behavior")
213
213
  ap.add_argument("name", metavar="NAME")
@@ -282,7 +282,7 @@ _CONV_RE = re.compile(
282
282
 
283
283
 
284
284
  def cmd_infer(argv: List[str]) -> int:
285
- ap = argparse.ArgumentParser(
285
+ ap = cliparse.parser(
286
286
  prog="boost infer",
287
287
  description="Generate a SKILL.md from your codebase patterns")
288
288
  ap.add_argument("--path", default=".", metavar="DIR",
@@ -510,7 +510,7 @@ _TRIVIAL = {"yes", "no", "ok", "okay", "continue", "go ahead", "sounds good",
510
510
 
511
511
 
512
512
  def cmd_absorb(argv: List[str]) -> int:
513
- ap = argparse.ArgumentParser(
513
+ ap = cliparse.parser(
514
514
  prog="boost absorb",
515
515
  description="Turn recurring chat-history patterns into a skill")
516
516
  ap.add_argument("--history", metavar="PATH",
@@ -645,7 +645,7 @@ def _absorb_template(name: str, patterns: List[Tuple[str, int]]) -> str:
645
645
  # ---------------------------------------------------------------- evolve
646
646
 
647
647
  def cmd_evolve(argv: List[str]) -> int:
648
- ap = argparse.ArgumentParser(
648
+ ap = cliparse.parser(
649
649
  prog="boost evolve",
650
650
  description="Iteratively improve a skill from feedback")
651
651
  ap.add_argument("name", metavar="NAME")
@@ -747,7 +747,7 @@ _CONTEXT_DEFAULT = {"enabled": False, "rules": []}
747
747
 
748
748
 
749
749
  def cmd_context(argv: List[str]) -> int:
750
- ap = argparse.ArgumentParser(
750
+ ap = cliparse.parser(
751
751
  prog="boost context", description="Branch-aware skill activation")
752
752
  sub = ap.add_subparsers(dest="action",
753
753
  metavar="status|enable|disable|map|unmap|apply")
@@ -890,7 +890,7 @@ _FOCUS_STATE = "focus.json"
890
890
 
891
891
 
892
892
  def cmd_focus(argv: List[str]) -> int:
893
- ap = argparse.ArgumentParser(
893
+ ap = cliparse.parser(
894
894
  prog="boost focus",
895
895
  description="Temporarily prioritize skills for a work session")
896
896
  ap.add_argument("skills", nargs="*", metavar="SKILL",
@@ -962,7 +962,7 @@ def cmd_focus(argv: List[str]) -> int:
962
962
  # ---------------------------------------------------------------- impact
963
963
 
964
964
  def cmd_impact(argv: List[str]) -> int:
965
- ap = argparse.ArgumentParser(
965
+ ap = cliparse.parser(
966
966
  prog="boost impact",
967
967
  description="Measure a skill's influence on code quality")
968
968
  ap.add_argument("name", nargs="?", metavar="NAME",
@@ -7,7 +7,6 @@ deterministic environment fingerprint.
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
- import argparse
11
10
  import hashlib
12
11
  import json
13
12
  import os
@@ -16,6 +15,7 @@ from datetime import datetime, timedelta, timezone
16
15
  from pathlib import Path
17
16
  from typing import List, Optional, Tuple
18
17
 
18
+ from .. import cliparse
19
19
  from ..core import (agents, ai, catalog, frontmatter, gitutil, journal,
20
20
  lockfile, logs, output as out, paths, policy, registry,
21
21
  store, util)
@@ -234,7 +234,7 @@ def _decay_rows(cwd: Path) -> List[dict]:
234
234
  # --- commands ---------------------------------------------------------------
235
235
 
236
236
  def cmd_doctor(argv):
237
- ap = argparse.ArgumentParser(
237
+ ap = cliparse.parser(
238
238
  prog="boost doctor", description="Check installation health & report issues")
239
239
  ap.parse_args(argv)
240
240
  issues = 0
@@ -352,7 +352,7 @@ def cmd_doctor(argv):
352
352
 
353
353
 
354
354
  def cmd_lint(argv):
355
- ap = argparse.ArgumentParser(
355
+ ap = cliparse.parser(
356
356
  prog="boost lint", description="Validate SKILL.md frontmatter & quality")
357
357
  ap.add_argument("names", nargs="*", metavar="NAME")
358
358
  ap.add_argument("--tap", metavar="TAP", help="lint every skill in a tap's clone")
@@ -428,7 +428,7 @@ def cmd_lint(argv):
428
428
 
429
429
 
430
430
  def cmd_audit(argv):
431
- ap = argparse.ArgumentParser(
431
+ ap = cliparse.parser(
432
432
  prog="boost audit",
433
433
  description="Check installed skills against a safety blocklist")
434
434
  ap.add_argument("--json", action="store_true")
@@ -501,7 +501,7 @@ def cmd_audit(argv):
501
501
 
502
502
 
503
503
  def cmd_verify(argv):
504
- ap = argparse.ArgumentParser(
504
+ ap = cliparse.parser(
505
505
  prog="boost verify",
506
506
  description="Validate skill quality & lock-file integrity")
507
507
  ap.add_argument("names", nargs="*", metavar="NAME")
@@ -547,7 +547,7 @@ def cmd_verify(argv):
547
547
 
548
548
 
549
549
  def cmd_drift(argv):
550
- ap = argparse.ArgumentParser(
550
+ ap = cliparse.parser(
551
551
  prog="boost drift",
552
552
  description="Detect installed skills diverging from source")
553
553
  ap.add_argument("names", nargs="*", metavar="NAME")
@@ -576,7 +576,7 @@ def cmd_drift(argv):
576
576
 
577
577
 
578
578
  def cmd_test(argv):
579
- ap = argparse.ArgumentParser(
579
+ ap = cliparse.parser(
580
580
  prog="boost test",
581
581
  description="Validate installed skills against quality checks")
582
582
  ap.add_argument("names", nargs="*", metavar="NAME")
@@ -613,7 +613,7 @@ def cmd_test(argv):
613
613
 
614
614
 
615
615
  def cmd_fingerprint(argv):
616
- ap = argparse.ArgumentParser(
616
+ ap = cliparse.parser(
617
617
  prog="boost fingerprint",
618
618
  description="Deterministic hash of the skill environment")
619
619
  ap.add_argument("--verbose", action="store_true",
@@ -636,7 +636,7 @@ def cmd_fingerprint(argv):
636
636
 
637
637
 
638
638
  def cmd_quarantine(argv):
639
- ap = argparse.ArgumentParser(
639
+ ap = cliparse.parser(
640
640
  prog="boost quarantine",
641
641
  description="Isolate a problematic skill without uninstalling")
642
642
  ap.add_argument("name", nargs="?", metavar="NAME")
@@ -700,7 +700,7 @@ def cmd_quarantine(argv):
700
700
 
701
701
 
702
702
  def cmd_decay(argv):
703
- ap = argparse.ArgumentParser(
703
+ ap = cliparse.parser(
704
704
  prog="boost decay",
705
705
  description="Flag skills irrelevant to your current stack")
706
706
  ap.add_argument("--json", action="store_true")
@@ -731,7 +731,7 @@ def cmd_decay(argv):
731
731
 
732
732
 
733
733
  def cmd_heal(argv):
734
- ap = argparse.ArgumentParser(
734
+ ap = cliparse.parser(
735
735
  prog="boost heal",
736
736
  description="Self-diagnose & repair the boost environment")
737
737
  ap.add_argument("--dry-run", action="store_true",
@@ -810,7 +810,7 @@ def cmd_heal(argv):
810
810
 
811
811
 
812
812
  def cmd_conflict(argv):
813
- ap = argparse.ArgumentParser(
813
+ ap = cliparse.parser(
814
814
  prog="boost conflict",
815
815
  description="Detect contradictory rules between skills")
816
816
  ap.add_argument("--json", action="store_true")
@@ -899,7 +899,7 @@ def cmd_conflict(argv):
899
899
 
900
900
 
901
901
  def cmd_changelog(argv):
902
- ap = argparse.ArgumentParser(
902
+ ap = cliparse.parser(
903
903
  prog="boost changelog",
904
904
  description="Show a skill's upstream change history")
905
905
  ap.add_argument("name", metavar="NAME")
@@ -933,7 +933,7 @@ def cmd_changelog(argv):
933
933
 
934
934
 
935
935
  def cmd_attest(argv):
936
- ap = argparse.ArgumentParser(
936
+ ap = cliparse.parser(
937
937
  prog="boost attest",
938
938
  description="Display/verify the install record for skills")
939
939
  ap.add_argument("name", nargs="?", metavar="NAME")
@@ -988,7 +988,7 @@ def cmd_attest(argv):
988
988
 
989
989
 
990
990
  def cmd_health(argv):
991
- ap = argparse.ArgumentParser(
991
+ ap = cliparse.parser(
992
992
  prog="boost health", description="Dashboard of skill-environment health")
993
993
  ap.parse_args(argv)
994
994
 
@@ -5,6 +5,7 @@ import argparse
5
5
  import json
6
6
  import os
7
7
 
8
+ from .. import cliparse
8
9
  from ..core import catalog, config, gitutil, journal, lockfile, paths, registry, store, util
9
10
  from ..core import output as out
10
11
  from ..errors import BoostError
@@ -73,7 +74,7 @@ def _catalog_selection(args) -> list:
73
74
 
74
75
  def cmd_tap(argv) -> int:
75
76
  """boost tap [SPEC] [--defaults] [--catalog] [--curated]"""
76
- p = argparse.ArgumentParser(
77
+ p = cliparse.parser(
77
78
  prog="boost tap",
78
79
  description="Add a GitHub repo as a skill registry")
79
80
  p.add_argument("spec", nargs="?",
@@ -126,7 +127,7 @@ def cmd_tap(argv) -> int:
126
127
 
127
128
  def cmd_untap(argv) -> int:
128
129
  """boost untap NAME [--force]"""
129
- p = argparse.ArgumentParser(
130
+ p = cliparse.parser(
130
131
  prog="boost untap",
131
132
  description="Remove a registry tap")
132
133
  p.add_argument("name", help="tap name (owner/repo or short alias)")
@@ -172,7 +173,7 @@ def _tap_updated(tap: "registry.Tap") -> str:
172
173
 
173
174
  def cmd_taps(argv) -> int:
174
175
  """boost taps [--json]"""
175
- p = argparse.ArgumentParser(
176
+ p = cliparse.parser(
176
177
  prog="boost taps",
177
178
  description="List all configured registry taps")
178
179
  p.add_argument("--json", action="store_true",
@@ -205,7 +206,7 @@ def cmd_taps(argv) -> int:
205
206
 
206
207
  def cmd_outdated(argv) -> int:
207
208
  """boost outdated [--json]"""
208
- p = argparse.ArgumentParser(
209
+ p = cliparse.parser(
209
210
  prog="boost outdated",
210
211
  description="Show skills with available updates")
211
212
  p.add_argument("--json", action="store_true",
@@ -1,7 +1,6 @@
1
1
  """Team & Collaboration commands: cohort, profile, protocol, pulse, replay, who."""
2
2
  from __future__ import annotations
3
3
 
4
- import argparse
5
4
  import getpass
6
5
  import hashlib
7
6
  import json
@@ -11,6 +10,7 @@ import shutil
11
10
  import stat
12
11
  import urllib.parse
13
12
 
13
+ from .. import cliparse
14
14
  from ..core import catalog, journal, lockfile, paths, registry, store, util
15
15
  from ..core import output as out
16
16
  from ..errors import BoostError
@@ -75,7 +75,7 @@ def _is_member(user: str, cohort_name: str, percent: int) -> bool:
75
75
 
76
76
  def cmd_cohort(argv) -> int:
77
77
  """boost cohort [list|create NAME --skills a,b --percent N|delete NAME|status|apply [NAME]]"""
78
- p = argparse.ArgumentParser(
78
+ p = cliparse.parser(
79
79
  prog="boost cohort",
80
80
  description="Controlled skill rollouts & team A/B testing",
81
81
  epilog="Membership is a deterministic hash of user+cohort, so a 50%% "
@@ -217,7 +217,7 @@ def _profile_diff(profile: dict):
217
217
 
218
218
  def cmd_profile(argv) -> int:
219
219
  """boost profile [list|save NAME|use NAME [--prune]|show NAME|diff NAME|delete NAME]"""
220
- p = argparse.ArgumentParser(
220
+ p = cliparse.parser(
221
221
  prog="boost profile",
222
222
  description="Named skill profiles for context switching")
223
223
  p.add_argument("action", nargs="?", default="list",
@@ -369,7 +369,7 @@ def _parse_boost_url(url: str):
369
369
 
370
370
  def cmd_protocol(argv) -> int:
371
371
  """boost protocol [status|register|unregister|open URL]"""
372
- p = argparse.ArgumentParser(
372
+ p = cliparse.parser(
373
373
  prog="boost protocol",
374
374
  description="Manage the boost:// one-click-install handler")
375
375
  p.add_argument("action", nargs="?", default="status",
@@ -472,7 +472,7 @@ _ACTION_COLOR = {"install": out.GREEN, "uninstall": out.RED,
472
472
 
473
473
  def cmd_pulse(argv) -> int:
474
474
  """boost pulse [-n N] [--all] [--action A] [--json]"""
475
- p = argparse.ArgumentParser(
475
+ p = cliparse.parser(
476
476
  prog="boost pulse",
477
477
  description="Team activity feed of skill-management events")
478
478
  p.add_argument("-n", type=int, default=20, help="events to show (default 20)")
@@ -510,7 +510,7 @@ def cmd_pulse(argv) -> int:
510
510
 
511
511
  def cmd_replay(argv) -> int:
512
512
  """boost replay [list|show ID|rollback ID]"""
513
- p = argparse.ArgumentParser(
513
+ p = cliparse.parser(
514
514
  prog="boost replay",
515
515
  description="View version history & roll back skills")
516
516
  p.add_argument("action", nargs="?", default="list",
@@ -619,7 +619,7 @@ def cmd_replay(argv) -> int:
619
619
 
620
620
  def cmd_who(argv) -> int:
621
621
  """boost who [SKILL] [--json]"""
622
- p = argparse.ArgumentParser(
622
+ p = cliparse.parser(
623
623
  prog="boost who",
624
624
  description="Discover who on the team has skill expertise")
625
625
  p.add_argument("skill", nargs="?", help="focus on one skill")
@@ -67,20 +67,30 @@ def rgb(r: int, g: int, b: int) -> str:
67
67
  return "\033[38;2;%d;%d;%dm" % (r, g, b)
68
68
 
69
69
 
70
- # Each Aurora token pairs its exact truecolor value with a 16-color fallback,
71
- # so the brand still reads on terminals without truecolor. Hexes mirror
72
- # style/boost.css: --cyan #22d3ee, --violet #a855f7, --pink #f472d0,
73
- # --green #4ade80, --yellow #facc15.
70
+ # Aurora palette — the single source of truth for boost's terminal colors.
71
+ # These RGB triples mirror the web design system's :root tokens in
72
+ # style/boost.css and must be kept in lockstep with it; everything else here
73
+ # (the gradient, brand tints, badges) is derived from them, never re-typed.
74
+ TOKENS = {
75
+ "cyan": (0x22, 0xd3, 0xee), # --cyan #22d3ee
76
+ "violet": (0xa8, 0x55, 0xf7), # --violet #a855f7
77
+ "pink": (0xf4, 0x72, 0xd0), # --pink #f472d0
78
+ "green": (0x4a, 0xde, 0x80), # --green #4ade80
79
+ "yellow": (0xfa, 0xcc, 0x15), # --yellow #facc15
80
+ }
81
+
82
+ # Each Aurora token pairs its truecolor value with a 16-color fallback, so the
83
+ # brand still reads on terminals without truecolor.
74
84
  _AURORA = {
75
- "cyan": ((0x22, 0xd3, 0xee), CYAN),
76
- "violet": ((0xa8, 0x55, 0xf7), MAGENTA),
77
- "pink": ((0xf4, 0x72, 0xd0), MAGENTA),
78
- "green": ((0x4a, 0xde, 0x80), GREEN),
79
- "yellow": ((0xfa, 0xcc, 0x15), YELLOW),
85
+ "cyan": (TOKENS["cyan"], CYAN),
86
+ "violet": (TOKENS["violet"], MAGENTA),
87
+ "pink": (TOKENS["pink"], MAGENTA),
88
+ "green": (TOKENS["green"], GREEN),
89
+ "yellow": (TOKENS["yellow"], YELLOW),
80
90
  }
81
91
 
82
92
  # The signature cyan -> violet -> pink gradient (style/boost.css --grad).
83
- _GRAD_STOPS = ((0x22, 0xd3, 0xee), (0xa8, 0x55, 0xf7), (0xf4, 0x72, 0xd0))
93
+ _GRAD_STOPS = (TOKENS["cyan"], TOKENS["violet"], TOKENS["pink"])
84
94
 
85
95
 
86
96
  def aurora(text: str, name: str, stream=None) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.33
3
+ Version: 1.0.35
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -436,11 +436,11 @@
436
436
  </article>
437
437
 
438
438
  <article class="ritem" data-track="commands" data-impact="med">
439
- <div class="ritem-top"><span class="rid">BOOST-D25</span><span class="rstatus progress">In progress</span></div>
439
+ <div class="ritem-top"><span class="rid">BOOST-D25</span><span class="rstatus done">Shipped</span></div>
440
440
  <h4>Branded usage &amp; error output</h4>
441
441
  <p>Grading confirmed this hits <b>every command with a required argument</b> — <code>boost uninstall</code>, <code>boost snapshot</code> and more all dump raw, unstyled <code>argparse</code>: a multi-line usage block with a bare <code>error:</code>, nothing like the branded top-level dispatch (which already routes through <code>Error:</code> + a difflib "did you mean"). The durable fix is a shared <code>BoostArgumentParser</code> subclass whose <code>error()</code>/usage route through the Aurora output layer, adopted across the ~50 per-command parsers — a broad, test-first sweep, so logged rather than rushed.</p>
442
442
  <div class="rmeta"><span class="m-cat">ux</span><span>Complexity L</span><span class="m-imp med">Impact Med</span><span class="m-wow">★★★☆☆</span></div>
443
- <div class="rref"><code>cliparse.py (BoostArgumentParser) · pkg.py done · 9 files to go</code></div>
443
+ <div class="rref"><code>cliparse.py (BoostArgumentParser) · all 10 command files</code></div>
444
444
  </article>
445
445
 
446
446
  </div>
@@ -467,11 +467,11 @@
467
467
  </article>
468
468
 
469
469
  <article class="ritem" data-track="system" data-impact="med">
470
- <div class="ritem-top"><span class="rid">BOOST-D20</span><span class="rstatus proposed">Proposed</span></div>
470
+ <div class="ritem-top"><span class="rid">BOOST-D20</span><span class="rstatus progress">In progress</span></div>
471
471
  <h4>Single source of design tokens (web ↔ CLI)</h4>
472
- <p>The palette lives only in CSS; the CLI would hard-code its own copy of the same hexes — guaranteeing drift. Emit both from one <code>tokens.json</code>: generate the CSS custom properties <em>and</em> the Python ANSI map from it, so a brand change updates the site and the terminal in lockstep. Fits the repo's existing "generate, never hand-edit" pattern (cf. <code>build_registries.py</code>).</p>
472
+ <p><b>Done:</b> the CLI palette is now single-sourced — one <code>output.TOKENS</code> dict holds every Aurora RGB triple, and the gradient, brand tints and badges all <em>derive</em> from it (previously cyan/violet/pink were re-typed across <code>_AURORA</code> and <code>_GRAD_STOPS</code>). A test locks the contract so a stray hex can't drift. <b>Remaining:</b> lift <code>TOKENS</code> into a shared <code>tokens.json</code> and generate <em>both</em> the CSS custom properties and this map from it, so the site and the terminal move in lockstep (the repo's "generate, never hand-edit" pattern, cf. <code>build_registries.py</code>).</p>
473
473
  <div class="rmeta"><span class="m-cat">system</span><span>Complexity M</span><span class="m-imp med">Impact Med</span><span class="m-wow">★★★★☆</span></div>
474
- <div class="rref"><code>scripts/ · new build_tokens.py</code></div>
474
+ <div class="rref"><code>core/output.py TOKENS · style/boost.css · future build_tokens.py</code></div>
475
475
  </article>
476
476
 
477
477
  <article class="ritem" data-track="system" data-impact="med">
@@ -139,6 +139,29 @@ class TestAurora:
139
139
  output.MAGENTA + "x" + output.RESET)
140
140
 
141
141
 
142
+ class TestTokens:
143
+ def test_known_hexes(self):
144
+ assert output.TOKENS["cyan"] == (0x22, 0xd3, 0xee)
145
+ assert output.TOKENS["violet"] == (0xa8, 0x55, 0xf7)
146
+ assert output.TOKENS["pink"] == (0xf4, 0x72, 0xd0)
147
+ assert output.TOKENS["green"] == (0x4a, 0xde, 0x80)
148
+ assert output.TOKENS["yellow"] == (0xfa, 0xcc, 0x15)
149
+
150
+ def test_aurora_derives_from_tokens(self, monkeypatch):
151
+ # every brand color resolves to rgb() of its TOKENS triple — proving
152
+ # TOKENS is the single source, not a re-typed copy.
153
+ monkeypatch.setenv("COLORTERM", "truecolor")
154
+ monkeypatch.setenv("CLICOLOR_FORCE", "1")
155
+ for name, (r, g, b) in output.TOKENS.items():
156
+ assert output.aurora("x", name) == (
157
+ output.rgb(r, g, b) + "x" + output.RESET)
158
+
159
+ def test_gradient_stops_are_tokens(self):
160
+ assert output._GRAD_STOPS == (output.TOKENS["cyan"],
161
+ output.TOKENS["violet"],
162
+ output.TOKENS["pink"])
163
+
164
+
142
165
  class TestGradient:
143
166
  def test_plain_when_no_color(self, monkeypatch):
144
167
  monkeypatch.setenv("NO_COLOR", "1")
File without changes