boost-skill-cli 1.0.30__tar.gz → 1.0.31__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 (105) hide show
  1. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/pkg.py +4 -1
  4. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  5. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/design-roadmap.html +3 -3
  6. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/roadmap.html +137 -1
  7. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_pkg.py +1 -0
  8. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/.gitignore +0 -0
  9. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/CLAUDE.md +0 -0
  10. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/CONTRIBUTING.md +0 -0
  11. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/LICENSE +0 -0
  12. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/MANIFEST.in +0 -0
  13. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/Makefile +0 -0
  14. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/README.md +0 -0
  15. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/SECURITY.md +0 -0
  16. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost +0 -0
  17. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/__init__.py +0 -0
  18. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/__main__.py +0 -0
  19. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/cli.py +0 -0
  20. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/__init__.py +0 -0
  21. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/bmad.py +0 -0
  22. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/configuration.py +0 -0
  23. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/discovery.py +0 -0
  24. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/hooks.py +0 -0
  25. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/info.py +0 -0
  26. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/intelligence.py +0 -0
  27. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/quality.py +0 -0
  28. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/taps.py +0 -0
  29. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/commands/team.py +0 -0
  30. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/__init__.py +0 -0
  31. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/agents.py +0 -0
  32. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/ai.py +0 -0
  33. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/catalog.py +0 -0
  34. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/claude_settings.py +0 -0
  35. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/config.py +0 -0
  36. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/dense.py +0 -0
  37. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/embed.py +0 -0
  38. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/frontmatter.py +0 -0
  39. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/gitutil.py +0 -0
  40. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/journal.py +0 -0
  41. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/lockfile.py +0 -0
  42. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/logs.py +0 -0
  43. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/output.py +0 -0
  44. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/paths.py +0 -0
  45. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/policy.py +0 -0
  46. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/rag.py +0 -0
  47. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/registry.py +0 -0
  48. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/store.py +0 -0
  49. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/core/util.py +0 -0
  50. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/data/registries.json +0 -0
  51. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_cli/errors.py +0 -0
  52. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
  53. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  54. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  55. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/requires.txt +0 -0
  56. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/boost_skill_cli.egg-info/top_level.txt +0 -0
  57. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/.claude/settings.local.json +0 -0
  58. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/DEBUGGING.md +0 -0
  59. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/demo.gif +0 -0
  60. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/demo.tape +0 -0
  61. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/index.html +0 -0
  62. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/docs/rag-architecture.md +0 -0
  63. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/pyproject.toml +0 -0
  64. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/scripts/build_registries.py +0 -0
  65. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/scripts/mutation_gate.py +0 -0
  66. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/setup.cfg +0 -0
  67. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/style/README.md +0 -0
  68. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/style/boost.css +0 -0
  69. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/style/boost.js +0 -0
  70. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/style/demo.html +0 -0
  71. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/conftest.py +0 -0
  72. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_bmad.py +0 -0
  73. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_configuration.py +0 -0
  74. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_discovery.py +0 -0
  75. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_hooks.py +0 -0
  76. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_info.py +0 -0
  77. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_intelligence.py +0 -0
  78. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_quality.py +0 -0
  79. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_taps.py +0 -0
  80. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_cli_team.py +0 -0
  81. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/functional/test_everyday_loop.py +0 -0
  82. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/make_fixture.py +0 -0
  83. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/smoke.sh +0 -0
  84. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_agents.py +0 -0
  85. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_ai.py +0 -0
  86. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_catalog.py +0 -0
  87. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_claude_settings.py +0 -0
  88. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_config.py +0 -0
  89. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_dense.py +0 -0
  90. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_embed.py +0 -0
  91. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_errors_and_cli_table.py +0 -0
  92. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_frontmatter.py +0 -0
  93. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_gitutil.py +0 -0
  94. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_journal.py +0 -0
  95. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_lockfile.py +0 -0
  96. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_logs.py +0 -0
  97. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_mutation_hardening.py +0 -0
  98. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_output.py +0 -0
  99. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_paths.py +0 -0
  100. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_policy.py +0 -0
  101. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_rag.py +0 -0
  102. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_registry.py +0 -0
  103. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_store.py +0 -0
  104. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/tests/unit/test_util.py +0 -0
  105. {boost_skill_cli-1.0.30 → boost_skill_cli-1.0.31}/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.30
3
+ Version: 1.0.31
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.30'
22
- __version_tuple__ = version_tuple = (1, 0, 30)
21
+ __version__ = version = '1.0.31'
22
+ __version_tuple__ = version_tuple = (1, 0, 31)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -176,7 +176,10 @@ def cmd_uninstall(argv: List[str]) -> int:
176
176
  out.ok("removed %s" % _tilde(dest))
177
177
  removed += 1
178
178
  if removed:
179
- out.info("Uninstalled %s" % _plural(removed, "skill"))
179
+ lines = ["Uninstalled %s" % _plural(removed, "skill")]
180
+ if removed == 1 and len(args.names) == 1:
181
+ lines.append(out.c("next: boost list", out.DIM))
182
+ print(out.panel(lines, title="removed", hue="pink"))
180
183
  return 1 if failed else 0
181
184
 
182
185
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.30
3
+ Version: 1.0.31
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -438,9 +438,9 @@
438
438
  <article class="ritem" data-track="commands" data-impact="med">
439
439
  <div class="ritem-top"><span class="rid">BOOST-D25</span><span class="rstatus proposed">Proposed</span></div>
440
440
  <h4>Branded usage &amp; error output</h4>
441
- <p>Run a subcommand wrong — <code>boost snapshot</code> with no action — and you get raw, unstyled <code>argparse</code>: a multi-line usage dump with a bare <code>error:</code>, nothing like the branded help. Route argparse's usage/error through the Aurora output layer (accent the program name, dim the usage grammar, use the standard <code>Error:</code> affordance) so <em>failure</em> looks as designed as success. Bonus: a "did you mean …" suggestion for unknown commands.</p>
442
- <div class="rmeta"><span class="m-cat">ux</span><span>Complexity M</span><span class="m-imp med">Impact Med</span><span class="m-wow">★★★☆☆</span></div>
443
- <div class="rref"><code>cli.py · argparse formatter</code></div>
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
+ <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>core · new BoostArgumentParser · commands/*.py</code></div>
444
444
  </article>
445
445
 
446
446
  </div>
@@ -116,7 +116,7 @@
116
116
  <div class="stat"><b>4</b><span>Shipped</span></div>
117
117
  <div class="stat"><b>3</b><span>Next up</span></div>
118
118
  <div class="stat"><b>4</b><span>Planned</span></div>
119
- <div class="stat"><b>16</b><span>Loop finds</span></div>
119
+ <div class="stat"><b>24</b><span>Loop finds</span></div>
120
120
  </div>
121
121
  </section>
122
122
 
@@ -596,6 +596,142 @@
596
596
  </div>
597
597
  </section>
598
598
 
599
+ <!-- ============ DEVELOPER EXPERIENCE & MAINTAINABILITY ============ -->
600
+ <section class="block reveal" id="dx">
601
+ <div class="block-head">
602
+ <h2>Developer experience &amp; maintainability</h2>
603
+ <span class="count">// planned · free tooling to catch issues earlier &amp; keep the code legible</span>
604
+ <span class="rule"></span>
605
+ </div>
606
+ <div class="rgrid">
607
+
608
+ <article class="cap rcard">
609
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">DX · Infra</span></div>
610
+ <h3>Shift-left gate — <code>pre-commit</code> + pre-commit.ci</h3>
611
+ <p>A <code>.pre-commit-config.yaml</code> runs ruff, mypy, codespell and the
612
+ whitespace fixers <em>before</em> code ever reaches CI, and the free-for-OSS
613
+ <strong>pre-commit.ci</strong> auto-fixes and auto-updates hooks right on the
614
+ PR. The foundation the rest of this section plugs into — one config,
615
+ many checks, faster feedback.</p>
616
+ <div class="meta">
617
+ <span class="m">Complexity <b>S</b></span>
618
+ <span class="m hi">Impact <b>High</b></span>
619
+ <span class="m">Wow <b class="wow">★★★★</b></span>
620
+ <span class="m">auto-fix PRs</span>
621
+ </div>
622
+ </article>
623
+
624
+ <article class="cap rcard">
625
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Quality · Architecture</span></div>
626
+ <h3>Layering guard — <code>import-linter</code></h3>
627
+ <p>Enforce the architecture boost already assumes: <code>core/</code> must
628
+ never import <code>commands/</code>, and the CLI depends inward only.
629
+ <code>import-linter</code> turns that contract into a CI check, catching
630
+ the cross-layer imports that slowly erode a clean engine — a structural
631
+ smell no line-level linter can see.</p>
632
+ <div class="meta">
633
+ <span class="m">Complexity <b>M</b></span>
634
+ <span class="m">Impact <b>Med</b></span>
635
+ <span class="m">Wow <b class="wow">★★★★</b></span>
636
+ <span class="m">core/ ↛ commands/</span>
637
+ </div>
638
+ </article>
639
+
640
+ <article class="cap rcard">
641
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Quality · Docs</span></div>
642
+ <h3>Typo detection — <code>codespell</code></h3>
643
+ <p>Cheap, high-signal for a project this documentation-heavy: scan the
644
+ README, <code>docs/*.html</code>, docstrings and every user-facing CLI
645
+ string for misspellings. A typo in <code>boost --help</code> is a bug
646
+ users see on their first run.</p>
647
+ <div class="meta">
648
+ <span class="m">Complexity <b>S</b></span>
649
+ <span class="m">Impact <b>Med</b></span>
650
+ <span class="m">Wow <b class="wow">★★</b></span>
651
+ <span class="m">user-facing text</span>
652
+ </div>
653
+ </article>
654
+
655
+ <article class="cap rcard">
656
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Quality · Docs</span></div>
657
+ <h3>Docstring coverage — <code>interrogate</code></h3>
658
+ <p>Gate CI below a docstring-coverage threshold so the engine stays
659
+ self-documenting as it grows. <code>core/</code> is already well
660
+ commented; <code>interrogate</code> keeps it that way and flags the
661
+ new public function that shipped without a word of explanation.</p>
662
+ <div class="meta">
663
+ <span class="m">Complexity <b>S</b></span>
664
+ <span class="m">Impact <b>Low</b></span>
665
+ <span class="m">Wow <b class="wow">★★</b></span>
666
+ <span class="m">keeps core/ documented</span>
667
+ </div>
668
+ </article>
669
+
670
+ <article class="cap rcard">
671
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Testing · Perf</span></div>
672
+ <h3>Performance-regression gate — <code>pytest-benchmark</code></h3>
673
+ <p>Benchmark the hot paths — a catalog scan over a large skills tree, a
674
+ registry load — and fail the build when a change regresses them past a
675
+ threshold. Catches the <em>performance</em> bugs a correctness suite
676
+ waves through: the O(n²) that only bites at scale.</p>
677
+ <div class="meta">
678
+ <span class="m">Complexity <b>M</b></span>
679
+ <span class="m">Impact <b>Med</b></span>
680
+ <span class="m">Wow <b class="wow">★★★</b></span>
681
+ <span class="m">scan &amp; registry</span>
682
+ </div>
683
+ </article>
684
+
685
+ <article class="cap rcard">
686
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Quality · Smell</span></div>
687
+ <h3>Modernization smells — <code>refurb</code> + <code>pyupgrade</code></h3>
688
+ <p><code>refurb</code>'s unique <code>FURB</code> checks flag dated idioms
689
+ the ruff families miss, and <code>pyupgrade</code> rewrites to the
690
+ cleanest form the <code>&gt;=3.9</code> floor allows. Keeps the codebase
691
+ reading like modern Python instead of accreting legacy patterns.</p>
692
+ <div class="meta">
693
+ <span class="m">Complexity <b>S</b></span>
694
+ <span class="m">Impact <b>Low</b></span>
695
+ <span class="m">Wow <b class="wow">★★</b></span>
696
+ <span class="m">respects 3.9 floor</span>
697
+ </div>
698
+ </article>
699
+
700
+ <article class="cap rcard">
701
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Testing · Platform</span></div>
702
+ <h3>Coverage dashboard — Codecov (free for OSS)</h3>
703
+ <p>Upgrade the homegrown coverage badge to Codecov's PR diff-coverage
704
+ comments, file sunburst and trend graph — free for public repos. The
705
+ hosted counterpart to the self-contained <code>diff-cover</code> gate:
706
+ one enforces patch coverage in CI, the other visualizes the trend over
707
+ time.</p>
708
+ <div class="meta">
709
+ <span class="m">Complexity <b>S</b></span>
710
+ <span class="m">Impact <b>Med</b></span>
711
+ <span class="m">Wow <b class="wow">★★★</b></span>
712
+ <span class="m">PR decoration</span>
713
+ </div>
714
+ </article>
715
+
716
+ <article class="cap rcard">
717
+ <div class="head"><span class="pill planned">Planned</span><span class="cat">Security · CI/CD</span></div>
718
+ <h3>Runner egress monitoring — StepSecurity Harden-Runner</h3>
719
+ <p>Free for public repos, Harden-Runner watches (and can block) unexpected
720
+ network egress from CI runners — the runtime signal that catches a
721
+ compromised action exfiltrating a token. The dynamic complement to
722
+ <code>zizmor</code>'s static workflow analysis, guarding the release
723
+ path from both sides.</p>
724
+ <div class="meta">
725
+ <span class="m">Complexity <b>S</b></span>
726
+ <span class="m">Impact <b>Med</b></span>
727
+ <span class="m">Wow <b class="wow">★★★★</b></span>
728
+ <span class="m">runtime + static</span>
729
+ </div>
730
+ </article>
731
+
732
+ </div>
733
+ </section>
734
+
599
735
  <footer>
600
736
  <p>Maintained by the boost quality loop · themed with
601
737
  <a href="../style/boost.css">style/boost.css</a> ·
@@ -102,6 +102,7 @@ class TestUninstall:
102
102
  assert "nope: nope is not installed" in r.out
103
103
  assert "unlinked ← claude-code · windsurf · cursor" in r.out
104
104
  assert "Uninstalled 2 skills" in r.out
105
+ assert "╭─ removed" in r.out # D13: framed summary card
105
106
  assert _lock() == {}
106
107
  assert not (paths.store_dir() / "brainstorming").exists()
107
108
  assert not (paths.store_dir() / "commit-messages").exists()
File without changes