memstack-skill-loader 3.5.0__tar.gz → 3.5.2__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 (24) hide show
  1. {memstack_skill_loader-3.5.0/src/memstack_skill_loader.egg-info → memstack_skill_loader-3.5.2}/PKG-INFO +1 -1
  2. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/README.md +10 -13
  3. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/pyproject.toml +1 -1
  4. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/config.py +51 -3
  5. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/server.py +9 -0
  6. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/version_check.py +16 -25
  7. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2/src/memstack_skill_loader.egg-info}/PKG-INFO +1 -1
  8. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/MANIFEST.in +0 -0
  9. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/setup.cfg +0 -0
  10. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/__init__.py +0 -0
  11. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/__main__.py +0 -0
  12. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/compression.py +0 -0
  13. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/dashboard.html +0 -0
  14. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/dashboard.py +0 -0
  15. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/indexer.py +0 -0
  16. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/license.py +0 -0
  17. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/search.py +0 -0
  18. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/stats.py +0 -0
  19. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader/tfidf_search.py +0 -0
  20. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader.egg-info/SOURCES.txt +0 -0
  21. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
  22. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
  23. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader.egg-info/requires.txt +0 -0
  24. {memstack_skill_loader-3.5.0 → memstack_skill_loader-3.5.2}/src/memstack_skill_loader.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memstack-skill-loader
3
- Version: 3.5.0
3
+ Version: 3.5.2
4
4
  Summary: MCP server that vector-indexes MemStack Pro skills for on-demand loading
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: mcp>=1.0.0
@@ -4,27 +4,24 @@
4
4
 
5
5
  ## Quick Start (5 minutes)
6
6
 
7
- 1. **Clone both repos side-by-side:**
7
+ 1. **Install from PyPI:**
8
8
  ```bash
9
- git clone https://github.com/cwinvestments/memstack.git
10
- git clone https://github.com/cwinvestments/memstack-skill-loader.git
9
+ pip install memstack-skill-loader
11
10
  ```
12
11
 
13
- 2. **Install the skill loader:**
14
- ```bash
15
- cd memstack-skill-loader
16
- pip install -e . --break-system-packages
17
- ```
18
-
19
- 3. **Register with Claude Code:**
12
+ 2. **Register with Claude Code:**
20
13
  ```bash
21
14
  claude mcp add --scope user memstack-skills -- python -m memstack_skill_loader
22
15
  ```
23
16
 
24
- 4. **Restart Claude Code**, then type `list skills` to verify.
17
+ 3. **Restart Claude Code**, then type `list skills` to verify.
18
+
19
+ 4. **Activate Pro** (if purchased at [memstack.pro](https://memstack.pro)):
20
+ ```
21
+ activate_license(key="your-key-here", email="you@example.com")
22
+ ```
25
23
 
26
- > The skill loader auto-detects the `memstack` repo if cloned as a sibling directory.
27
- > To use a different location, set `MEMSTACK_SKILLS_DIR=/path/to/memstack/skills`.
24
+ > To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
28
25
 
29
26
  > See [QUICKSTART.md](QUICKSTART.md) for detailed setup, [QUICK-REFERENCE.md](QUICK-REFERENCE.md) for the full skill catalog, and [TROUBLESHOOTING.md](TROUBLESHOOTING.md) if you hit issues.
30
27
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memstack-skill-loader"
7
- version = "3.5.0"
7
+ version = "3.5.2"
8
8
  description = "MCP server that vector-indexes MemStack Pro skills for on-demand loading"
9
9
  requires-python = ">=3.12"
10
10
  dependencies = [
@@ -60,14 +60,62 @@ class Config:
60
60
  )
61
61
 
62
62
 
63
+ def _auto_discover_skills() -> SkillSource | None:
64
+ """Scan common locations for MemStack skills when no config.json exists (PyPI installs)."""
65
+ env_dir = os.environ.get("MEMSTACK_SKILLS_DIR")
66
+ if env_dir:
67
+ p = Path(env_dir).expanduser()
68
+ if p.exists():
69
+ skill_files = list(p.glob("**/SKILL.md"))
70
+ if skill_files:
71
+ print(f"Using MEMSTACK_SKILLS_DIR: {p} ({len(skill_files)} skills found)", file=sys.stderr)
72
+ return SkillSource(type="local", path=str(p), pattern="**/SKILL.md", label="MemStack")
73
+
74
+ home = Path.home()
75
+ candidates = [
76
+ home / ".claude" / "plugins" / "cache" / "cwinvestments-memstack" / "memstack",
77
+ home / ".claude" / "plugins" / "marketplaces" / "cwinvestments-memstack" / "skills",
78
+ home / "memstack" / "skills",
79
+ Path.cwd().parent / "memstack" / "skills",
80
+ ]
81
+
82
+ for candidate in candidates:
83
+ if candidate.name == "memstack":
84
+ version_dirs = sorted(candidate.glob("*/skills"), reverse=True)
85
+ for vdir in version_dirs:
86
+ skill_files = list(vdir.glob("**/SKILL.md"))
87
+ if skill_files:
88
+ print(f"Auto-detected skills at: {vdir} ({len(skill_files)} skills found)", file=sys.stderr)
89
+ return SkillSource(type="local", path=str(vdir), pattern="**/SKILL.md", label="MemStack")
90
+ else:
91
+ if candidate.exists():
92
+ skill_files = list(candidate.glob("**/SKILL.md"))
93
+ if skill_files:
94
+ print(f"Auto-detected skills at: {candidate} ({len(skill_files)} skills found)", file=sys.stderr)
95
+ return SkillSource(type="local", path=str(candidate), pattern="**/SKILL.md", label="MemStack")
96
+
97
+ print(
98
+ "No skills found. Install the MemStack plugin first: "
99
+ "/plugin marketplace add cwinvestments/memstack",
100
+ file=sys.stderr,
101
+ )
102
+ return None
103
+
104
+
63
105
  def load_config(config_path: Path | None = None) -> Config:
64
- """Load config from JSON file. Falls back to defaults if not found."""
106
+ """Load config from JSON file. Falls back to auto-discovery if not found."""
65
107
  if config_path is None:
66
108
  config_path = Path(__file__).resolve().parent.parent.parent / "config.json"
67
109
 
68
110
  if not config_path.exists():
69
- print(f"Config not found at {config_path}, using defaults", file=sys.stderr)
70
- return Config()
111
+ source = _auto_discover_skills()
112
+ config = Config(
113
+ skill_sources=[source] if source else [],
114
+ auto_reindex_on_start=True,
115
+ )
116
+ if os.environ.get("MEMSTACK_PRO_LICENSE_KEY"):
117
+ config = config.with_pro_skills()
118
+ return config
71
119
 
72
120
  try:
73
121
  with open(config_path, encoding="utf-8") as f:
@@ -874,6 +874,15 @@ async def run():
874
874
  from .stats import backfill_categories
875
875
  backfill_categories(_CATEGORY_MAP)
876
876
 
877
+ # Auto-build index on first run (PyPI installs have no pre-built index).
878
+ if config.auto_reindex_on_start:
879
+ from .tfidf_search import _get_index as _peek_index
880
+ if _peek_index(config) is None:
881
+ from .indexer import build_index
882
+ print("[memstack] Building skill index for first time...", file=sys.stderr)
883
+ count = build_index(config)
884
+ print(f"[memstack] Indexed {count} skills.", file=sys.stderr)
885
+
877
886
  # Pre-load TF-IDF index before stdio_server starts its stdin reader thread.
878
887
  # On Windows, a blocking readline() in the stdin thread causes GIL contention
879
888
  # that slows down CPU-intensive operations (like sklearn import) by ~20x.
@@ -1,28 +1,22 @@
1
- """Check for MemStack updates via GitHub releases API."""
1
+ """Check for MemStack updates via the PyPI JSON API."""
2
2
 
3
3
  import json
4
- import os
5
4
  import sys
6
5
  import time
7
6
  from pathlib import Path
8
7
 
9
- GITHUB_API_URL = "https://api.github.com/repos/cwinvestments/memstack/releases/latest"
8
+ PYPI_URL = "https://pypi.org/pypi/memstack-skill-loader/json"
10
9
  CACHE_FILE = Path.home() / ".memstack" / "version-check.json"
11
- CACHE_TTL = 86400 # 24 hours in seconds
10
+ CACHE_TTL = 86400 # 24 hours
12
11
 
13
12
 
14
- def _read_local_version() -> str | None:
15
- """Read the VERSION file from the parent memstack repo directory."""
16
- # The skill loader lives inside memstack, so walk up to find VERSION
17
- loader_dir = Path(__file__).resolve().parent
18
- for parent in [loader_dir] + list(loader_dir.parents):
19
- version_file = parent / "VERSION"
20
- if version_file.exists():
21
- try:
22
- return version_file.read_text(encoding="utf-8").strip()
23
- except OSError:
24
- return None
25
- return None
13
+ def _get_local_version() -> str | None:
14
+ """Get the installed package version via importlib.metadata."""
15
+ try:
16
+ from importlib.metadata import version
17
+ return version("memstack-skill-loader")
18
+ except Exception:
19
+ return None
26
20
 
27
21
 
28
22
  def _read_cache() -> dict | None:
@@ -51,32 +45,30 @@ def _write_cache(remote_version: str) -> None:
51
45
 
52
46
 
53
47
  def check_for_updates() -> None:
54
- """Check GitHub for a newer MemStack version. Prints to stderr if update available."""
48
+ """Check PyPI for a newer MemStack version. Prints to stderr if update available."""
55
49
  try:
56
- local_version = _read_local_version()
50
+ local_version = _get_local_version()
57
51
  if not local_version:
58
52
  return
59
53
 
60
- # Check cache first
61
54
  cached = _read_cache()
62
55
  if cached:
63
56
  remote_version = cached.get("remote_version", "")
64
57
  if remote_version and remote_version != local_version:
65
58
  print(
66
59
  f"[memstack] Update available: {remote_version} (you have {local_version}). "
67
- f"Run 'git pull' in your MemStack\u2122 directory to update.",
60
+ f"Run: pip install memstack-skill-loader --upgrade",
68
61
  file=sys.stderr,
69
62
  )
70
63
  return
71
64
 
72
- # Fetch from GitHub API
73
65
  import httpx
74
- response = httpx.get(GITHUB_API_URL, timeout=5.0, follow_redirects=True)
66
+ response = httpx.get(PYPI_URL, timeout=5.0, follow_redirects=True)
75
67
  if response.status_code != 200:
76
68
  return
77
69
 
78
70
  data = response.json()
79
- remote_version = data.get("tag_name", "")
71
+ remote_version = data.get("info", {}).get("version", "")
80
72
  if not remote_version:
81
73
  return
82
74
 
@@ -85,9 +77,8 @@ def check_for_updates() -> None:
85
77
  if remote_version != local_version:
86
78
  print(
87
79
  f"[memstack] Update available: {remote_version} (you have {local_version}). "
88
- f"Run 'git pull' in your MemStack\u2122 directory to update.",
80
+ f"Run: pip install memstack-skill-loader --upgrade",
89
81
  file=sys.stderr,
90
82
  )
91
83
  except Exception:
92
- # Silently skip on any error — never block skill loading
93
84
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memstack-skill-loader
3
- Version: 3.5.0
3
+ Version: 3.5.2
4
4
  Summary: MCP server that vector-indexes MemStack Pro skills for on-demand loading
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: mcp>=1.0.0