boost-skill-cli 1.0.52__tar.gz → 1.0.54__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.52 → boost_skill_cli-1.0.54}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/config.py +3 -2
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/frontmatter.py +8 -3
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/journal.py +3 -1
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/lockfile.py +27 -2
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/util.py +32 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/SOURCES.txt +2 -0
- boost_skill_cli-1.0.54/docs/mcp-hub.html +473 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/roadmap.html +20 -1
- boost_skill_cli-1.0.54/tests/unit/test_dense_fallback.py +463 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_frontmatter.py +11 -6
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_lockfile.py +25 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_util.py +44 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/.gitignore +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/LICENSE +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/Makefile +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/README.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/index.html +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/index.html +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/setup.cfg +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/style/README.md +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/style/boost.css +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/style/boost.js +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/style/demo.html +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.52 → boost_skill_cli-1.0.54}/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.54'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 54)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
from copy import deepcopy
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from . import paths
|
|
8
|
+
from . import paths, util
|
|
9
9
|
|
|
10
10
|
DEFAULTS = {
|
|
11
11
|
"agents": {
|
|
@@ -90,7 +90,8 @@ def load() -> dict:
|
|
|
90
90
|
|
|
91
91
|
def save(cfg: dict) -> None:
|
|
92
92
|
paths.ensure_dirs()
|
|
93
|
-
|
|
93
|
+
util.atomic_write_text(
|
|
94
|
+
paths.config_path(), json.dumps(cfg, indent=2, sort_keys=False) + "\n")
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def get(dotted: str, default=None):
|
|
@@ -35,12 +35,17 @@ def _scalar(raw: str):
|
|
|
35
35
|
return ""
|
|
36
36
|
if len(s) >= 2 and s[0] == s[-1] and s[0] in "'\"":
|
|
37
37
|
return s[1:-1]
|
|
38
|
+
# YAML 1.2 core schema only: true/false and null/~. The 1.1 aliases
|
|
39
|
+
# (yes/no/on/off, none) are NOT coerced — they are ordinary English words
|
|
40
|
+
# that legitimately appear as a skill's name or tag ("on", "none"), and
|
|
41
|
+
# silently turning them into bool/None leaks the wrong type into search
|
|
42
|
+
# and ranking meta.
|
|
38
43
|
low = s.lower()
|
|
39
|
-
if low
|
|
44
|
+
if low == "true":
|
|
40
45
|
return True
|
|
41
|
-
if low
|
|
46
|
+
if low == "false":
|
|
42
47
|
return False
|
|
43
|
-
if low
|
|
48
|
+
if low == "null" or s == "~":
|
|
44
49
|
return None
|
|
45
50
|
try:
|
|
46
51
|
return int(s)
|
|
@@ -67,4 +67,6 @@ def _maybe_rotate() -> None:
|
|
|
67
67
|
except OSError:
|
|
68
68
|
return
|
|
69
69
|
if len(lines) > ROTATE_AT:
|
|
70
|
-
|
|
70
|
+
# Atomic swap: a bare write_text here races concurrent log() appends and
|
|
71
|
+
# can truncate the feed mid-line if interrupted.
|
|
72
|
+
util.atomic_write_text(p, "\n".join(lines[-ROTATE_KEEP:]) + "\n")
|
|
@@ -26,16 +26,41 @@ def _skeleton() -> dict:
|
|
|
26
26
|
def read() -> dict:
|
|
27
27
|
p = paths.lockfile_path()
|
|
28
28
|
if not p.exists():
|
|
29
|
-
return _skeleton()
|
|
29
|
+
return _skeleton() # empty: never installed anything yet
|
|
30
30
|
try:
|
|
31
31
|
lock = json.loads(p.read_text())
|
|
32
32
|
except (json.JSONDecodeError, OSError):
|
|
33
|
+
# Corrupt (present but unparseable) is NOT the same as empty: returning
|
|
34
|
+
# a bare skeleton here would let the next write() overwrite the only
|
|
35
|
+
# record of every prior install. Preserve the bytes for recovery and
|
|
36
|
+
# surface it loudly before falling back to the skeleton.
|
|
37
|
+
_preserve_corrupt(p)
|
|
33
38
|
return _skeleton()
|
|
34
39
|
lock.setdefault("version", SCHEMA_VERSION)
|
|
35
40
|
lock.setdefault("skills", {})
|
|
36
41
|
return lock
|
|
37
42
|
|
|
38
43
|
|
|
44
|
+
def _preserve_corrupt(p) -> None:
|
|
45
|
+
"""Copy an unparseable lock file aside as ``<lock>.corrupt`` and warn.
|
|
46
|
+
|
|
47
|
+
Best effort: a read must still succeed (returning the skeleton) even if the
|
|
48
|
+
sidecar cannot be written, so failures here are swallowed after logging.
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
backup = p.with_name(p.name + ".corrupt")
|
|
52
|
+
shutil.copy(p, backup)
|
|
53
|
+
except OSError:
|
|
54
|
+
backup = None
|
|
55
|
+
try:
|
|
56
|
+
from . import logs
|
|
57
|
+
logs.get_logger().warning(
|
|
58
|
+
"lock file %s is corrupt; preserved %s and continuing with an "
|
|
59
|
+
"empty lock", p, backup or "(backup failed)")
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
|
|
39
64
|
def write(lock: dict) -> None:
|
|
40
65
|
paths.ensure_dirs()
|
|
41
66
|
p = paths.lockfile_path()
|
|
@@ -52,7 +77,7 @@ def write(lock: dict) -> None:
|
|
|
52
77
|
_prune_history()
|
|
53
78
|
lock["version"] = SCHEMA_VERSION
|
|
54
79
|
lock["updated"] = util.now_iso()
|
|
55
|
-
|
|
80
|
+
util.atomic_write_text(p, json.dumps(lock, indent=2, sort_keys=True) + "\n")
|
|
56
81
|
|
|
57
82
|
|
|
58
83
|
def _history_files() -> List:
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import hashlib
|
|
5
|
+
import os
|
|
5
6
|
import re
|
|
7
|
+
import tempfile
|
|
6
8
|
from datetime import datetime, timezone
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
from typing import List, Tuple
|
|
@@ -10,6 +12,36 @@ from typing import List, Tuple
|
|
|
10
12
|
IGNORED = {".git", "__pycache__", ".DS_Store"}
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
def atomic_write_text(path: Path, text: str, encoding: str = "utf-8") -> None:
|
|
16
|
+
"""Write ``text`` to ``path`` so a reader never sees a partial file.
|
|
17
|
+
|
|
18
|
+
The bytes go to a temp file in the *same* directory (so the rename stays on
|
|
19
|
+
one filesystem and is atomic), get flushed and fsynced to disk, then swap
|
|
20
|
+
into place with ``os.replace``. An interrupted or concurrent write can no
|
|
21
|
+
longer truncate the target: the previous file survives intact until the
|
|
22
|
+
complete new one atomically replaces it. This is the corruption-safe
|
|
23
|
+
substitute for a bare ``Path.write_text`` on any file boost must not lose —
|
|
24
|
+
the lock file, the config, the pulse journal.
|
|
25
|
+
"""
|
|
26
|
+
path = Path(path)
|
|
27
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
fd, tmp = tempfile.mkstemp(dir=str(path.parent),
|
|
29
|
+
prefix="." + path.name + ".", suffix=".tmp")
|
|
30
|
+
try:
|
|
31
|
+
with os.fdopen(fd, "w", encoding=encoding) as f:
|
|
32
|
+
f.write(text)
|
|
33
|
+
f.flush()
|
|
34
|
+
os.fsync(f.fileno())
|
|
35
|
+
os.replace(tmp, str(path))
|
|
36
|
+
except BaseException:
|
|
37
|
+
# Never leave a temp turd behind, and never mask the original error.
|
|
38
|
+
try:
|
|
39
|
+
os.unlink(tmp)
|
|
40
|
+
except OSError:
|
|
41
|
+
pass
|
|
42
|
+
raise
|
|
43
|
+
|
|
44
|
+
|
|
13
45
|
def now_iso() -> str:
|
|
14
46
|
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
15
47
|
|
|
@@ -61,6 +61,7 @@ docs/demo.gif
|
|
|
61
61
|
docs/demo.tape
|
|
62
62
|
docs/design-roadmap.html
|
|
63
63
|
docs/index.html
|
|
64
|
+
docs/mcp-hub.html
|
|
64
65
|
docs/rag-architecture.md
|
|
65
66
|
docs/roadmap.html
|
|
66
67
|
docs/.claude/settings.local.json
|
|
@@ -91,6 +92,7 @@ tests/unit/test_claude_settings.py
|
|
|
91
92
|
tests/unit/test_cliparse.py
|
|
92
93
|
tests/unit/test_config.py
|
|
93
94
|
tests/unit/test_dense.py
|
|
95
|
+
tests/unit/test_dense_fallback.py
|
|
94
96
|
tests/unit/test_embed.py
|
|
95
97
|
tests/unit/test_errors_and_cli_table.py
|
|
96
98
|
tests/unit/test_frontmatter.py
|