memstack-skill-loader 4.15.0__tar.gz → 4.16.0__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 (72) hide show
  1. {memstack_skill_loader-4.15.0/src/memstack_skill_loader.egg-info → memstack_skill_loader-4.16.0}/PKG-INFO +11 -5
  2. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/README.md +10 -4
  3. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/pyproject.toml +1 -1
  4. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/__init__.py +1 -1
  5. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/config.py +28 -2
  6. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/dashboard.html +21 -1
  7. memstack_skill_loader-4.16.0/src/memstack_skill_loader/diary_ingest.py +467 -0
  8. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/server.py +72 -3
  9. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0/src/memstack_skill_loader.egg-info}/PKG-INFO +11 -5
  10. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader.egg-info/SOURCES.txt +1 -0
  11. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_diary_ingest.py +249 -0
  12. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_memory_tools.py +32 -0
  13. memstack_skill_loader-4.16.0/tests/test_pro_source_resolution.py +227 -0
  14. memstack_skill_loader-4.15.0/src/memstack_skill_loader/diary_ingest.py +0 -278
  15. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/MANIFEST.in +0 -0
  16. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/setup.cfg +0 -0
  17. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/__main__.py +0 -0
  18. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/agent_runner.py +0 -0
  19. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/categories.py +0 -0
  20. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/compression.py +0 -0
  21. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/dashboard.py +0 -0
  22. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/goal.py +0 -0
  23. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/goal_engine.py +0 -0
  24. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/hooks/__init__.py +0 -0
  25. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/hooks/deny_git.py +0 -0
  26. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/hooks/deny_manager_write.py +0 -0
  27. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/hooks/deny_reviewer_git.py +0 -0
  28. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/hooks/git_policy.py +0 -0
  29. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/index_meta.py +0 -0
  30. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/indexer.py +0 -0
  31. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/license.py +0 -0
  32. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/memory_db.py +0 -0
  33. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/memory_engine.py +0 -0
  34. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/__init__.py +0 -0
  35. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/_diag.py +0 -0
  36. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/body_parser.py +0 -0
  37. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/compressor.py +0 -0
  38. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/forwarder.py +0 -0
  39. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/pro_compressor.py +0 -0
  40. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/server.py +0 -0
  41. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/proxy/stats_tracker.py +0 -0
  42. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/search.py +0 -0
  43. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/session_context.py +0 -0
  44. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/skill_config.py +0 -0
  45. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/skill_descriptions.json +0 -0
  46. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/stats.py +0 -0
  47. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/tfidf_search.py +0 -0
  48. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader/version_check.py +0 -0
  49. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
  50. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
  51. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader.egg-info/requires.txt +0 -0
  52. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/src/memstack_skill_loader.egg-info/top_level.txt +0 -0
  53. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_advisor_strategy.py +0 -0
  54. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_body_parser_metric.py +0 -0
  55. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_builder_scope.py +0 -0
  56. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_code_read_savings.py +0 -0
  57. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_dashscan.py +0 -0
  58. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_failed_approaches.py +0 -0
  59. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_git_policy.py +0 -0
  60. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_index_meta_guard.py +0 -0
  61. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_license_environment.py +0 -0
  62. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_license_grace.py +0 -0
  63. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_memory_engine.py +0 -0
  64. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_memory_inspector.py +0 -0
  65. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_memory_scoring.py +0 -0
  66. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_pro_compressor.py +0 -0
  67. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_pro_skills_update.py +0 -0
  68. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_project_dir_canonical.py +0 -0
  69. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_search_memory.py +0 -0
  70. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_session_context.py +0 -0
  71. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_skill_drift.py +0 -0
  72. {memstack_skill_loader-4.15.0 → memstack_skill_loader-4.16.0}/tests/test_wheel_contents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memstack-skill-loader
3
- Version: 4.15.0
3
+ Version: 4.16.0
4
4
  Summary: MCP server that vector-indexes MemStack Pro skills for on-demand loading
5
5
  Project-URL: Homepage, https://memstack.pro
6
6
  Project-URL: Documentation, https://memstack.pro/docs
@@ -72,7 +72,7 @@ activate_license(key="your-key", email="you@example.com")
72
72
  ```
73
73
  Use `key="free"` for the free tier, or your Pro key from [memstack.pro](https://memstack.pro) to unlock all 44 Pro skills (86 free + 44 Pro = 130 total). Then type `list skills` to verify.
74
74
 
75
- > **The first start is slow, and that is normal.** On the very first run after installing or upgrading, the server builds the skill index before it answers anything. Expect roughly a minute, nearly all of it loading the embedding model rather than reading your skills. **The server will look unresponsive for that time.** Let it finish. Every later start reuses the index and is not affected.
75
+ > **The first start is slow, and that is normal.** On the very first run after installing or upgrading, the server builds the skill index before it answers anything. Nearly all of that time is a one-time download of the ~90 MB embedding model, so it tracks your network speed rather than the size of your skill library: indexing 86 skills itself takes about a second. On a fast connection the whole first start is around half a minute; on a slow one it is longer. **The server will look unresponsive for that time.** Let it finish. Every later start reuses the cached model and index and is not affected.
76
76
 
77
77
  > To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
78
78
 
@@ -80,7 +80,7 @@ Use `key="free"` for the free tier, or your Pro key from [memstack.pro](https://
80
80
 
81
81
  ## Troubleshooting
82
82
 
83
- **Nothing responds after a fresh install, and it does not seem to be failing either (during Step 4):** the first start builds the skill index before the server answers anything, which takes roughly a minute. Most of that is loading the embedding model, not reading your skills. There is no progress output, so a working first start and a hung one look identical. Wait it out before changing anything. If it is still silent well past a couple of minutes, then treat it as the interpreter mismatch below. Later starts reuse the index and respond immediately.
83
+ **Nothing responds after a fresh install, and it does not seem to be failing either (during Step 4):** the first start builds the skill index before the server answers anything. Most of that is a one-time ~90 MB embedding-model download, not reading your skills, so the wait depends on your connection: around half a minute on a fast one, longer on a slow one. There is no progress output, so a working first start and a hung one look identical. Wait it out before changing anything. If it is still silent well past a few minutes, then treat it as the interpreter mismatch below. Later starts reuse the cached model and index and respond immediately.
84
84
 
85
85
  **`error: externally-managed-environment` (during Step 2)** — pip refuses to install on a system-managed Python. Add `--break-system-packages` · (in terminal):
86
86
  ```bash
@@ -111,7 +111,7 @@ MCP server that vector-indexes all 130 MemStack™ skills so Claude Code can cal
111
111
 
112
112
  - **No Anthropic API key required:** skill search and indexing run locally and never leave your machine. Three small calls happen on their own at session start — license validation, a Pro-bundle version probe, and a PyPI update check — and every other outbound call is one you trigger ([Data and privacy](#data-and-privacy))
113
113
  - **Pro skills auto-detected:** once activated, the Pro skills directory is added to your sources automatically, with no `config.json` edit
114
- - **Auto-reindex on start:** skills stay current without manual rebuilds. Only startup ever rebuilds, never a search. The first start pays a one-time build of roughly a minute; after that a rebuild happens in the background while the existing index keeps serving, so later starts are not delayed
114
+ - **Auto-reindex on start:** skills stay current without manual rebuilds. Only startup ever rebuilds, never a search. The first start pays a one-time build dominated by the embedding-model download (see [Quick Start](#quick-start-5-minutes)); after that a rebuild happens in the background while the existing index keeps serving, so later starts are not delayed
115
115
  - **Memory Engine:** an append-only, versioned fact store with query-time recall scoring — facts decay with age and decay more slowly the more they are recalled. Facts flow in from the diary's `## FACTS` block and are exposed to Claude through five `mem_*` tools (see [Available Tools](#available-tools))
116
116
  - **Living memory at session start:** the SessionStart hook injects a compact, token-budgeted digest of the project's top recall-scored facts into context. It fails open — a missing, empty, or unreachable store never blocks startup
117
117
  - **8-page localhost dashboard:** Overview, Skills Manager, Burn Report, Memory Browser (project-aware diary reader), Memory Inspector (read-only Memory Engine fact views), Agent Monitor, Referrals, and Settings
@@ -248,9 +248,15 @@ The `pattern` field controls how skills are discovered:
248
248
 
249
249
  ## Release Notes
250
250
 
251
+ ### 4.15.1 (August 2026)
252
+
253
+ - **Fixed: activating a Pro license on a PyPI install downloaded the Pro skills and never indexed them.** The skills landed in `~/.memstack/pro-skills`, activation reported success, and the catalog still showed only the 86 free skills. Setting `MEMSTACK_PRO_LICENSE_KEY` was the only workaround. Both of the paths that decide whether to attach the Pro source now share one predicate.
254
+ - **Activation now takes effect immediately.** The reindex that follows an activation rebuilds with the Pro skills included, and the confirmation reports the post-activation count, so a restart is no longer needed.
255
+ - First-start timing in these docs was reworded: it is dominated by a one-time embedding-model download and therefore varies with network speed, rather than being the fixed "roughly a minute" previously quoted.
256
+
251
257
  ### 4.15.0 (August 2026)
252
258
 
253
- - The skill index moved to the canonical state root, so a relative `vector_db_path` no longer depends on which directory the server happened to start in. **The first start after installing or upgrading rebuilds the index once, which takes roughly a minute**, nearly all of it loading the embedding model. Later starts are unaffected.
259
+ - The skill index moved to the canonical state root, so a relative `vector_db_path` no longer depends on which directory the server happened to start in. **The first start after installing or upgrading rebuilds the index once**, nearly all of that time being a one-time embedding-model download. Later starts are unaffected.
254
260
  - New index guard classifies the index as current, drift, stale, absent, or rebuilding, and reports that verdict at startup and in the dashboard. Only startup may rebuild on its own, so no search request ever blocks on one.
255
261
  - Agent Runner git policy rewritten as an argv-level classifier, replacing a substring match that could not tell `git status` from `git push`.
256
262
 
@@ -55,7 +55,7 @@ activate_license(key="your-key", email="you@example.com")
55
55
  ```
56
56
  Use `key="free"` for the free tier, or your Pro key from [memstack.pro](https://memstack.pro) to unlock all 44 Pro skills (86 free + 44 Pro = 130 total). Then type `list skills` to verify.
57
57
 
58
- > **The first start is slow, and that is normal.** On the very first run after installing or upgrading, the server builds the skill index before it answers anything. Expect roughly a minute, nearly all of it loading the embedding model rather than reading your skills. **The server will look unresponsive for that time.** Let it finish. Every later start reuses the index and is not affected.
58
+ > **The first start is slow, and that is normal.** On the very first run after installing or upgrading, the server builds the skill index before it answers anything. Nearly all of that time is a one-time download of the ~90 MB embedding model, so it tracks your network speed rather than the size of your skill library: indexing 86 skills itself takes about a second. On a fast connection the whole first start is around half a minute; on a slow one it is longer. **The server will look unresponsive for that time.** Let it finish. Every later start reuses the cached model and index and is not affected.
59
59
 
60
60
  > To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
61
61
 
@@ -63,7 +63,7 @@ Use `key="free"` for the free tier, or your Pro key from [memstack.pro](https://
63
63
 
64
64
  ## Troubleshooting
65
65
 
66
- **Nothing responds after a fresh install, and it does not seem to be failing either (during Step 4):** the first start builds the skill index before the server answers anything, which takes roughly a minute. Most of that is loading the embedding model, not reading your skills. There is no progress output, so a working first start and a hung one look identical. Wait it out before changing anything. If it is still silent well past a couple of minutes, then treat it as the interpreter mismatch below. Later starts reuse the index and respond immediately.
66
+ **Nothing responds after a fresh install, and it does not seem to be failing either (during Step 4):** the first start builds the skill index before the server answers anything. Most of that is a one-time ~90 MB embedding-model download, not reading your skills, so the wait depends on your connection: around half a minute on a fast one, longer on a slow one. There is no progress output, so a working first start and a hung one look identical. Wait it out before changing anything. If it is still silent well past a few minutes, then treat it as the interpreter mismatch below. Later starts reuse the cached model and index and respond immediately.
67
67
 
68
68
  **`error: externally-managed-environment` (during Step 2)** — pip refuses to install on a system-managed Python. Add `--break-system-packages` · (in terminal):
69
69
  ```bash
@@ -94,7 +94,7 @@ MCP server that vector-indexes all 130 MemStack™ skills so Claude Code can cal
94
94
 
95
95
  - **No Anthropic API key required:** skill search and indexing run locally and never leave your machine. Three small calls happen on their own at session start — license validation, a Pro-bundle version probe, and a PyPI update check — and every other outbound call is one you trigger ([Data and privacy](#data-and-privacy))
96
96
  - **Pro skills auto-detected:** once activated, the Pro skills directory is added to your sources automatically, with no `config.json` edit
97
- - **Auto-reindex on start:** skills stay current without manual rebuilds. Only startup ever rebuilds, never a search. The first start pays a one-time build of roughly a minute; after that a rebuild happens in the background while the existing index keeps serving, so later starts are not delayed
97
+ - **Auto-reindex on start:** skills stay current without manual rebuilds. Only startup ever rebuilds, never a search. The first start pays a one-time build dominated by the embedding-model download (see [Quick Start](#quick-start-5-minutes)); after that a rebuild happens in the background while the existing index keeps serving, so later starts are not delayed
98
98
  - **Memory Engine:** an append-only, versioned fact store with query-time recall scoring — facts decay with age and decay more slowly the more they are recalled. Facts flow in from the diary's `## FACTS` block and are exposed to Claude through five `mem_*` tools (see [Available Tools](#available-tools))
99
99
  - **Living memory at session start:** the SessionStart hook injects a compact, token-budgeted digest of the project's top recall-scored facts into context. It fails open — a missing, empty, or unreachable store never blocks startup
100
100
  - **8-page localhost dashboard:** Overview, Skills Manager, Burn Report, Memory Browser (project-aware diary reader), Memory Inspector (read-only Memory Engine fact views), Agent Monitor, Referrals, and Settings
@@ -231,9 +231,15 @@ The `pattern` field controls how skills are discovered:
231
231
 
232
232
  ## Release Notes
233
233
 
234
+ ### 4.15.1 (August 2026)
235
+
236
+ - **Fixed: activating a Pro license on a PyPI install downloaded the Pro skills and never indexed them.** The skills landed in `~/.memstack/pro-skills`, activation reported success, and the catalog still showed only the 86 free skills. Setting `MEMSTACK_PRO_LICENSE_KEY` was the only workaround. Both of the paths that decide whether to attach the Pro source now share one predicate.
237
+ - **Activation now takes effect immediately.** The reindex that follows an activation rebuilds with the Pro skills included, and the confirmation reports the post-activation count, so a restart is no longer needed.
238
+ - First-start timing in these docs was reworded: it is dominated by a one-time embedding-model download and therefore varies with network speed, rather than being the fixed "roughly a minute" previously quoted.
239
+
234
240
  ### 4.15.0 (August 2026)
235
241
 
236
- - The skill index moved to the canonical state root, so a relative `vector_db_path` no longer depends on which directory the server happened to start in. **The first start after installing or upgrading rebuilds the index once, which takes roughly a minute**, nearly all of it loading the embedding model. Later starts are unaffected.
242
+ - The skill index moved to the canonical state root, so a relative `vector_db_path` no longer depends on which directory the server happened to start in. **The first start after installing or upgrading rebuilds the index once**, nearly all of that time being a one-time embedding-model download. Later starts are unaffected.
237
243
  - New index guard classifies the index as current, drift, stale, absent, or rebuilding, and reports that verdict at startup and in the dashboard. Only startup may rebuild on its own, so no search request ever blocks on one.
238
244
  - Agent Runner git policy rewritten as an argv-level classifier, replacing a substring match that could not tell `git status` from `git push`.
239
245
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memstack-skill-loader"
7
- version = "4.15.0"
7
+ version = "4.16.0"
8
8
  description = "MCP server that vector-indexes MemStack Pro skills for on-demand loading"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """MemStack Skill Loader — MCP server for semantic skill search."""
2
2
 
3
- __version__ = "4.15.0"
3
+ __version__ = "4.16.0"
@@ -126,6 +126,32 @@ def _auto_discover_skills() -> SkillSource | None:
126
126
  return None
127
127
 
128
128
 
129
+ def _pro_license_present() -> bool:
130
+ """Return True if this installation has a Pro license on either supported path.
131
+
132
+ Both call sites in load_config must ask this question the same way. They did
133
+ not: the env-var-only form was written once, copied into the auto-discovery
134
+ branch, and then fixed in only one of the two copies (07676eb, 2026-05-20).
135
+ For the two and a half months after that, every PyPI install (which always
136
+ takes the auto-discovery branch, because a wheel has no config.json beside
137
+ the package) downloaded Pro skills on activation and never indexed them.
138
+ Keep this predicate the single source of truth so a third copy cannot drift.
139
+
140
+ The env var and license.json are both first-class: license.json is the
141
+ documented primary path (activate_license writes it), and the env var is the
142
+ documented alternative. This mirrors get_license_key() in license.py, which
143
+ has always resolved both.
144
+
145
+ Note this answers "is a license present", not "is it valid". Entitlement is
146
+ enforced at serve time against a live validate_license() call, so a stale
147
+ license.json here only affects what gets indexed, never what a caller is
148
+ allowed to read.
149
+ """
150
+ if os.environ.get("MEMSTACK_PRO_LICENSE_KEY"):
151
+ return True
152
+ return (Path.home() / ".memstack" / "license.json").exists()
153
+
154
+
129
155
  def load_config(config_path: Path | None = None) -> Config:
130
156
  """Load config from JSON file. Falls back to auto-discovery if not found."""
131
157
  if config_path is None:
@@ -138,7 +164,7 @@ def load_config(config_path: Path | None = None) -> Config:
138
164
  auto_reindex_on_start=True,
139
165
  config_source="auto-discovery",
140
166
  )
141
- if os.environ.get("MEMSTACK_PRO_LICENSE_KEY"):
167
+ if _pro_license_present():
142
168
  config = config.with_pro_skills()
143
169
  return config
144
170
 
@@ -184,7 +210,7 @@ def load_config(config_path: Path | None = None) -> Config:
184
210
  )
185
211
 
186
212
  # Auto-detect pro-skills if license key is set (env var or license.json)
187
- if os.environ.get("MEMSTACK_PRO_LICENSE_KEY") or (Path.home() / ".memstack" / "license.json").exists():
213
+ if _pro_license_present():
188
214
  config = config.with_pro_skills()
189
215
 
190
216
  return config
@@ -2141,7 +2141,7 @@
2141
2141
  <button class="mcp-tools-save-btn" style="background:#30363d;" onclick="verifyLicenseKey()">Verify</button>
2142
2142
  <span id="licenseKeyStatus" style="font-size:0.75rem;color:#484f58;"></span>
2143
2143
  </div>
2144
- <p style="color:#484f58;font-size:0.72rem;margin:0.35rem 0 0;">Unlocks Pro-tier compression and 42 exclusive skills. Stored at ~/.memstack/license.json.</p>
2144
+ <p style="color:#484f58;font-size:0.72rem;margin:0.35rem 0 0;">Unlocks Pro-tier compression and <span id="settings-pro-skill-count">all</span> exclusive skills. Stored at ~/.memstack/license.json.</p>
2145
2145
  </div>
2146
2146
  </div>
2147
2147
 
@@ -5370,6 +5370,7 @@ async function loadSettings() {
5370
5370
  loadGitWorkflowGlobal();
5371
5371
  loadAgentModes();
5372
5372
  loadLicenseStatus();
5373
+ loadProSkillCount();
5373
5374
  settingsLoaded = true;
5374
5375
  } catch (e) {
5375
5376
  console.error('Settings load failed:', e);
@@ -5396,6 +5397,25 @@ async function loadLicenseStatus() {
5396
5397
  }
5397
5398
  }
5398
5399
 
5400
+ // Pro skill count in the licence blurb, derived from /api/skills rather than
5401
+ // written into the markup -- a literal here read "42" while the enforced count
5402
+ // was 44. The static text says "all", so a failed or index-less fetch degrades
5403
+ // to a sentence that is still true rather than to a stale number.
5404
+ async function loadProSkillCount() {
5405
+ const el = document.getElementById('settings-pro-skill-count');
5406
+ if (!el) return;
5407
+ try {
5408
+ const res = await fetch('/api/skills', {headers: AUTH_GET});
5409
+ const d = await res.json();
5410
+ // Absent/unreadable index responses omit display_pro_count entirely and
5411
+ // report pro_count 0; neither should overwrite the fallback.
5412
+ const n = d.display_pro_count;
5413
+ if (Number.isFinite(n) && n > 0) el.textContent = n;
5414
+ } catch (e) {
5415
+ /* keep the "all" fallback; a count is not worth a broken panel */
5416
+ }
5417
+ }
5418
+
5399
5419
  async function saveLicenseKey() {
5400
5420
  const input = document.getElementById('licenseKeyInput');
5401
5421
  const statusEl = document.getElementById('licenseKeyStatus');