memstack-skill-loader 4.5.1__tar.gz → 4.7.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.
Potentially problematic release.
This version of memstack-skill-loader might be problematic. Click here for more details.
- {memstack_skill_loader-4.5.1/src/memstack_skill_loader.egg-info → memstack_skill_loader-4.7.0}/PKG-INFO +1 -1
- memstack_skill_loader-4.7.0/README.md +218 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/pyproject.toml +1 -1
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/__init__.py +1 -1
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/categories.py +40 -13
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/dashboard.html +21 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/dashboard.py +9 -1
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/license.py +49 -2
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/body_parser.py +24 -1
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/pro_compressor.py +174 -45
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/stats_tracker.py +58 -3
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/server.py +13 -2
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0/src/memstack_skill_loader.egg-info}/PKG-INFO +1 -1
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader.egg-info/SOURCES.txt +3 -0
- memstack_skill_loader-4.7.0/tests/test_body_parser_metric.py +138 -0
- memstack_skill_loader-4.7.0/tests/test_code_read_savings.py +64 -0
- memstack_skill_loader-4.7.0/tests/test_license_environment.py +86 -0
- memstack_skill_loader-4.7.0/tests/test_pro_compressor.py +688 -0
- memstack_skill_loader-4.5.1/README.md +0 -152
- memstack_skill_loader-4.5.1/tests/test_pro_compressor.py +0 -384
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/MANIFEST.in +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/setup.cfg +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/__main__.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/agent_runner.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/compression.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/config.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/indexer.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/memory_db.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/__init__.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/_diag.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/compressor.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/forwarder.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/proxy/server.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/search.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/skill_config.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/stats.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/tfidf_search.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/version_check.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader.egg-info/requires.txt +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader.egg-info/top_level.txt +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/tests/test_license_grace.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/tests/test_pro_skills_update.py +0 -0
- {memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/tests/test_skill_drift.py +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# MemStack™ Skill Loader
|
|
2
|
+
|
|
3
|
+
**128 skills for Claude Code:** 85 free + 43 Pro exclusive. Vector-indexed so CC loads only the skill it needs, saving your context window.
|
|
4
|
+
|
|
5
|
+
## Quick Start (5 minutes)
|
|
6
|
+
|
|
7
|
+
MemStack installs in two parts: the **skills** (via the Claude Code plugin marketplace) and the **engine** (this loader, via PyPI). You need both. Every command below is labeled **(in Claude Code)** or **(in terminal)** — running one in the wrong place is the most common setup mistake.
|
|
8
|
+
|
|
9
|
+
**Step 1 — Install the free skills · (in Claude Code):**
|
|
10
|
+
```
|
|
11
|
+
/plugin marketplace add cwinvestments/memstack
|
|
12
|
+
/plugin install memstack@cwinvestments-memstack
|
|
13
|
+
```
|
|
14
|
+
Run both commands. This unlocks the 85 free skills right away.
|
|
15
|
+
|
|
16
|
+
> **SSH error?** ("Host key verification failed" on a fresh server that's never used GitHub over SSH.)
|
|
17
|
+
> **Default fix · (in terminal)** — rewrite GitHub to HTTPS, then retry Step 1:
|
|
18
|
+
> ```bash
|
|
19
|
+
> git config --global url."https://github.com/".insteadOf "git@github.com:"
|
|
20
|
+
> ```
|
|
21
|
+
> **Backup fix · (in terminal)** — add GitHub's host key, then retry Step 1:
|
|
22
|
+
> ```bash
|
|
23
|
+
> mkdir -p ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
|
|
24
|
+
> ```
|
|
25
|
+
|
|
26
|
+
**Step 2 — Install the engine · (in terminal):**
|
|
27
|
+
|
|
28
|
+
Windows:
|
|
29
|
+
```bash
|
|
30
|
+
pip install memstack-skill-loader
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Linux / Mac — use the explicit interpreter (the same one you register in Step 3):
|
|
34
|
+
```bash
|
|
35
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
36
|
+
```
|
|
37
|
+
Find yours with `which python3` and substitute it if it isn't `/usr/bin/python3`. The `--break-system-packages` flag is required on newer externally-managed Python. No pip for that interpreter? Run `sudo apt install python3-pip` first (Debian/Ubuntu).
|
|
38
|
+
|
|
39
|
+
**Step 3 — Register the MCP server · (in terminal):** register against the **same interpreter you installed onto in Step 2.**
|
|
40
|
+
|
|
41
|
+
Windows:
|
|
42
|
+
```bash
|
|
43
|
+
claude mcp add --scope user memstack-skills -- python -m memstack_skill_loader
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Linux / Mac:
|
|
47
|
+
```bash
|
|
48
|
+
claude mcp add --scope user memstack-skills -- /usr/bin/python3 -m memstack_skill_loader
|
|
49
|
+
```
|
|
50
|
+
If this doesn't match Step 2's interpreter, the server won't launch (you'll see a "failed to reconnect" error and `activate_license` will be missing).
|
|
51
|
+
|
|
52
|
+
**Step 4 — Activate your license · (in Claude Code, after a full restart):** fully quit and reopen Claude Code first so it picks up the new MCP server, then run:
|
|
53
|
+
```
|
|
54
|
+
activate_license(key="your-key", email="you@example.com")
|
|
55
|
+
```
|
|
56
|
+
Use `key="free"` for the free tier, or your Pro key from [memstack.pro](https://memstack.pro) to unlock all 43 Pro skills (85 free + 43 Pro = 128 total). Then type `list skills` to verify.
|
|
57
|
+
|
|
58
|
+
> To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
|
|
59
|
+
|
|
60
|
+
> 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.
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
**`error: externally-managed-environment` (during Step 2)** — pip refuses to install on a system-managed Python. Add `--break-system-packages` · (in terminal):
|
|
65
|
+
```bash
|
|
66
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Plugin clone fails: "Host key verification failed" (during Step 1)** — a fresh server that's never connected to GitHub over SSH. **Default fix · (in terminal):** `git config --global url."https://github.com/".insteadOf "git@github.com:"` then retry Step 1. **Backup fix · (in terminal):** `mkdir -p ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts` then retry Step 1.
|
|
70
|
+
|
|
71
|
+
**`activate_license` not found, or MCP "failed to reconnect (-32000)"** — the loader was installed onto a **different Python interpreter** than the one Claude Code launches (the classic bare-`python` mismatch on Linux/Mac). Check · (in terminal):
|
|
72
|
+
```bash
|
|
73
|
+
/usr/bin/python3 -c "import memstack_skill_loader; print('ok')"
|
|
74
|
+
```
|
|
75
|
+
If it prints `ok`, make sure Step 3 registered that exact path, then fully restart Claude Code. If it errors with `ModuleNotFoundError`, reinstall onto the explicit interpreter and re-register against that same path · (in terminal):
|
|
76
|
+
```bash
|
|
77
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
78
|
+
claude mcp add --scope user memstack-skills -- /usr/bin/python3 -m memstack_skill_loader
|
|
79
|
+
```
|
|
80
|
+
Then fully restart Claude Code. Rule of thumb: whatever interpreter you `import`-check as `ok` is the path that must appear in your `claude mcp add` command.
|
|
81
|
+
|
|
82
|
+
**`No module named pip` (during Step 2)** — that interpreter ships without pip (common on minimal Debian/Ubuntu server images). Install it, then retry the Step 2 install · (in terminal):
|
|
83
|
+
```bash
|
|
84
|
+
sudo apt install python3-pip
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## How It Works
|
|
88
|
+
|
|
89
|
+
MCP server that vector-indexes all 128 MemStack™ skills so Claude Code can call `find_skill("deploy to Railway")` and load **only** the relevant skill on demand, instead of all skills consuming context window.
|
|
90
|
+
|
|
91
|
+
- **No Anthropic API key required:** skill search and indexing run locally — the only outbound network call is license validation, which contacts the MemStack license server on session start ([Data and privacy](#data-and-privacy))
|
|
92
|
+
- **Pro skills auto-detected:** set your license key and they appear automatically
|
|
93
|
+
- **Auto-reindex on start:** skills stay current without manual rebuilds
|
|
94
|
+
|
|
95
|
+
### Data and privacy
|
|
96
|
+
|
|
97
|
+
License validation is the loader's only outbound network call. On session start it POSTs to the MemStack license server (`admin.cwaffiliateinvestments.com/api/licenses/validate`) with:
|
|
98
|
+
|
|
99
|
+
- your **license key**
|
|
100
|
+
- a **machine ID** — a stable, hashed fingerprint that binds the license to your machine
|
|
101
|
+
- your **email**, if you provided one at activation
|
|
102
|
+
- your **environment** — OS name, Python version, and MemStack loader version
|
|
103
|
+
|
|
104
|
+
That is the complete payload. **No code, file contents, file paths, usernames, project names, or prompts are ever sent.** The environment fields exist only so platform-specific breakage (for example, a bug that affects only Linux) is visible to us in aggregate.
|
|
105
|
+
|
|
106
|
+
### Environment Variable Override
|
|
107
|
+
|
|
108
|
+
Set `MEMSTACK_SKILLS_DIR` to override the skills path in `config.json`:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
export MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Stack
|
|
115
|
+
|
|
116
|
+
- Python 3.12+
|
|
117
|
+
- [MCP SDK](https://pypi.org/project/mcp/) (stdio transport)
|
|
118
|
+
- [LanceDB](https://lancedb.com/) (vector storage, zero external dependencies)
|
|
119
|
+
- [sentence-transformers](https://www.sbert.net/) with `all-MiniLM-L6-v2` (384-dim local embeddings)
|
|
120
|
+
|
|
121
|
+
## Available Tools
|
|
122
|
+
|
|
123
|
+
### `find_skill`
|
|
124
|
+
Search skills by describing what you need. Returns the most relevant skill(s) with full instructions.
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
find_skill(query="deploy to Railway", top_k=3)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `list_skills`
|
|
131
|
+
Browse the full skill catalog with names and descriptions.
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
list_skills()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `get_skill`
|
|
138
|
+
Load a specific skill by exact name.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
get_skill(name="railway deploy")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `reindex_skills`
|
|
145
|
+
Rebuild the vector index after adding or modifying skills.
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
reindex_skills()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
The `config.json` file controls where skills are loaded from:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"skill_sources": [
|
|
158
|
+
{
|
|
159
|
+
"type": "local",
|
|
160
|
+
"path": "C:\\Projects\\memstack\\skills",
|
|
161
|
+
"pattern": "**/SKILL.md",
|
|
162
|
+
"label": "MemStack"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"embedding_model": "all-MiniLM-L6-v2",
|
|
166
|
+
"default_top_k": 3,
|
|
167
|
+
"vector_db_path": "./vectors",
|
|
168
|
+
"auto_reindex_on_start": true
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Pro skills are **auto-detected** when `MEMSTACK_PRO_LICENSE_KEY` is set, no need to add them to `config.json`.
|
|
173
|
+
|
|
174
|
+
Add entries to `skill_sources` to index skills from multiple directories:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"skill_sources": [
|
|
179
|
+
{
|
|
180
|
+
"type": "local",
|
|
181
|
+
"path": "C:\\Projects\\memstack\\skills",
|
|
182
|
+
"pattern": "**/SKILL.md",
|
|
183
|
+
"label": "MemStack"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"type": "local",
|
|
187
|
+
"path": "/home/user/custom-skills",
|
|
188
|
+
"pattern": "*.md",
|
|
189
|
+
"label": "My Custom Skills"
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The `pattern` field controls how skills are discovered:
|
|
196
|
+
- `**/SKILL.md`: Subdirectory structure (e.g., `category/skill-name/SKILL.md`)
|
|
197
|
+
- `*.md`: Flat directory (each `.md` file is a skill)
|
|
198
|
+
|
|
199
|
+
## Release Notes
|
|
200
|
+
|
|
201
|
+
### v4.0.0 (May 2026)
|
|
202
|
+
|
|
203
|
+
- Dashboard: 6-page localhost dashboard (Overview, Skills Manager, Burn Report, Memory Browser, Agent Monitor, Settings)
|
|
204
|
+
- Agent Runner: 3-agent orchestration (Manager/Builder/Reviewer) with per-agent model selection
|
|
205
|
+
- 17 MCP tools
|
|
206
|
+
- 127 skills (84 free + 43 Pro)
|
|
207
|
+
- Real-time context window monitoring per agent
|
|
208
|
+
- Session diary with AI-authored markdown narratives
|
|
209
|
+
- Safe git staging (prevents accidental commits of secrets/runtime data)
|
|
210
|
+
- Task completion notifications (browser, tab flash, audio)
|
|
211
|
+
- Token usage tracking with estimated costs
|
|
212
|
+
- TokenStack™ proxy integration (~35-40% token savings)
|
|
213
|
+
|
|
214
|
+
**[v3.4.0](https://github.com/cwinvestments/memstack-skill-loader/releases/tag/v3.4.0)**: 100 Skills Milestone (18 new Pro skills, auto-detection, display name fixes)
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
Proprietary. Part of MemStack™ Pro by CW Affiliate Investments LLC.
|
{memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/categories.py
RENAMED
|
@@ -12,7 +12,8 @@ CATEGORY_MAP = {
|
|
|
12
12
|
"quill": "Business", "scan": "Business", "governor": "Business",
|
|
13
13
|
"client-onboarding": "Business", "contract-template": "Business", "financial-model": "Business",
|
|
14
14
|
"freelancer-toolkit": "Business", "invoice-generator": "Business", "scope-of-work": "Business",
|
|
15
|
-
"sop-builder": "Business", "proposal-writer": "Business",
|
|
15
|
+
"sop-builder": "Business", "proposal-writer": "Business", "gdpr": "Business",
|
|
16
|
+
"licensing": "Business",
|
|
16
17
|
# Content
|
|
17
18
|
"humanize": "Content", "blog-post": "Content", "email-sequence": "Content",
|
|
18
19
|
"landing-page-copy": "Content", "newsletter": "Content", "product-description": "Content",
|
|
@@ -26,7 +27,7 @@ CATEGORY_MAP = {
|
|
|
26
27
|
"forge": "Development", "shard": "Development",
|
|
27
28
|
"state": "Development", "work": "Development", "verify": "Development",
|
|
28
29
|
"project": "Development", "familiar": "Development", "api-designer": "Development",
|
|
29
|
-
"code-reviewer": "Development", "
|
|
30
|
+
"code-reviewer": "Development", "migration-planner": "Development",
|
|
30
31
|
"performance-audit": "Development", "refactor-planner": "Development", "test-writer": "Development",
|
|
31
32
|
"changelog-generator": "Development", "mentor": "Development", "webapp-testing": "Development",
|
|
32
33
|
# Core
|
|
@@ -45,20 +46,46 @@ CATEGORY_MAP = {
|
|
|
45
46
|
"user-story-generator": "Product", "mvp-scoper": "Product", "roadmap-builder": "Product",
|
|
46
47
|
"feedback-analyzer": "Product",
|
|
47
48
|
# Security
|
|
48
|
-
"advanced-security": "Security", "env-manager-pro": "Security",
|
|
49
49
|
"api-audit": "Security", "csp-headers": "Security", "dependency-audit": "Security",
|
|
50
50
|
"owasp-top10": "Security", "owasp-top-10": "Security",
|
|
51
51
|
"rls-checker": "Security", "rls-guardian": "Security",
|
|
52
|
-
"secrets-scanner": "Security",
|
|
53
|
-
# Pro skills (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
"
|
|
52
|
+
"secrets-scanner": "Security", "git-guard": "Security",
|
|
53
|
+
# ── Pro-exclusive skills (43) — CANONICAL category map ─────────────────
|
|
54
|
+
# Must cover every slug in PRO_EXCLUSIVE_SKILLS (license.py) exactly; no Pro
|
|
55
|
+
# slug may fall through to "Other". Enforced by scripts/check_skill_drift.py
|
|
56
|
+
# (Check C / F-3). Core = MemStack's own session/memory/agent/efficiency
|
|
57
|
+
# meta-skills ONLY; anything that operates on the user's code/docs is
|
|
58
|
+
# Development, not Core.
|
|
59
|
+
# Core (9)
|
|
60
|
+
"burn": "Core", "checkpoint": "Core", "consolidate": "Core",
|
|
61
|
+
"context-db": "Core", "council": "Core", "governor-pro": "Core",
|
|
62
|
+
"model-router": "Core", "multi-agent": "Core", "session-restore": "Core",
|
|
63
|
+
# Development (21)
|
|
64
|
+
"api-docs": "Development", "api-load-tester": "Development",
|
|
65
|
+
"branching": "Development", "claude-api-helper": "Development",
|
|
66
|
+
"codebase-index": "Development", "database-architect": "Development",
|
|
67
|
+
"database-migration": "Development", "developer-growth-analysis": "Development",
|
|
68
|
+
"diagram-generator": "Development", "doc-index": "Development",
|
|
69
|
+
"drift-detection": "Development", "error-handler": "Development",
|
|
70
|
+
"frontend-design": "Development", "git-worktrees": "Development",
|
|
71
|
+
"log-analyzer": "Development", "mcp-builder": "Development",
|
|
72
|
+
"nextjs-conventions": "Development", "performance-profiler": "Development",
|
|
73
|
+
"python-conventions": "Development", "rag-builder": "Development",
|
|
74
|
+
"test-generator": "Development",
|
|
75
|
+
# Security (4)
|
|
76
|
+
"advanced-security": "Security", "config-audit": "Security",
|
|
77
|
+
"dependency-auditor": "Security", "env-manager-pro": "Security",
|
|
78
|
+
# Automation (4)
|
|
79
|
+
"browser-use": "Automation", "hooks-integration": "Automation",
|
|
80
|
+
"video-pipeline": "Automation", "web-scraper": "Automation",
|
|
81
|
+
# Business (2)
|
|
82
|
+
"meeting-insights-analyzer": "Business", "us-privacy-compliance": "Business",
|
|
83
|
+
# Content (1)
|
|
84
|
+
"social-media": "Content",
|
|
85
|
+
# Deployment (1)
|
|
86
|
+
"ios-app-store": "Deployment",
|
|
87
|
+
# Marketing (1)
|
|
88
|
+
"gtm-validator": "Marketing",
|
|
62
89
|
}
|
|
63
90
|
|
|
64
91
|
|
{memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/dashboard.html
RENAMED
|
@@ -1564,6 +1564,10 @@
|
|
|
1564
1564
|
<div class="card">
|
|
1565
1565
|
<div class="value stat-green" id="ts-avg-savings">-</div>
|
|
1566
1566
|
<div class="label">Avg Savings</div>
|
|
1567
|
+
<div id="ts-code-read" style="display:none;font-size:0.62rem;color:#6e7681;margin-top:0.35rem;line-height:1.3">
|
|
1568
|
+
<span id="ts-code-read-text"></span>
|
|
1569
|
+
<span id="ts-code-read-info" style="cursor:help;border-bottom:1px dotted #6e7681">ⓘ</span>
|
|
1570
|
+
</div>
|
|
1567
1571
|
</div>
|
|
1568
1572
|
<div class="card">
|
|
1569
1573
|
<div class="value" style="color:#58a6ff" id="ts-requests">-</div>
|
|
@@ -5216,6 +5220,21 @@ async function loadTokenStackSavings() {
|
|
|
5216
5220
|
avgEl.textContent = (d.avg_savings_pct || 0).toFixed(1) + '%';
|
|
5217
5221
|
reqEl.textContent = fmt(d.total_requests || 0);
|
|
5218
5222
|
|
|
5223
|
+
// Secondary, clearly-subordinate stat: all-time p95 of the AST-fired
|
|
5224
|
+
// (code-read) slice. Hidden unless the slice is big enough to be meaningful.
|
|
5225
|
+
const crEl = document.getElementById('ts-code-read');
|
|
5226
|
+
const crText = document.getElementById('ts-code-read-text');
|
|
5227
|
+
const crInfo = document.getElementById('ts-code-read-info');
|
|
5228
|
+
if (d.code_read_display_pct != null && (d.code_read_sample_size || 0) >= 50) {
|
|
5229
|
+
crText.textContent = 'up to ~' + d.code_read_display_pct + '% on code-heavy reads';
|
|
5230
|
+
crInfo.title = 'Code-heavy = requests where AST truncation fires (file reads of '
|
|
5231
|
+
+ 'source code). p95 of that slice (n=' + fmt(d.code_read_sample_size)
|
|
5232
|
+
+ ') — not the typical case.';
|
|
5233
|
+
crEl.style.display = '';
|
|
5234
|
+
} else {
|
|
5235
|
+
crEl.style.display = 'none';
|
|
5236
|
+
}
|
|
5237
|
+
|
|
5219
5238
|
const labels = {free: 'Free', ast: 'AST', json: 'JSON', log: 'Log', path: 'Path',
|
|
5220
5239
|
system: 'System Prompt', dedup: 'Conversation Dedup', markdown: 'Markdown'};
|
|
5221
5240
|
const order = ['free', 'ast', 'json', 'log', 'path', 'system', 'dedup', 'markdown'];
|
|
@@ -5230,6 +5249,8 @@ async function loadTokenStackSavings() {
|
|
|
5230
5249
|
savedEl.textContent = '-';
|
|
5231
5250
|
avgEl.textContent = '-';
|
|
5232
5251
|
reqEl.textContent = '-';
|
|
5252
|
+
const crEl = document.getElementById('ts-code-read');
|
|
5253
|
+
if (crEl) crEl.style.display = 'none';
|
|
5233
5254
|
tbody.innerHTML = '<tr><td colspan="2"><span class="empty-state">No compression data yet. Start the dashboard with --with-proxy.</span></td></tr>';
|
|
5234
5255
|
}
|
|
5235
5256
|
}
|
{memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/dashboard.py
RENAMED
|
@@ -30,7 +30,11 @@ from . import agent_runner
|
|
|
30
30
|
import subprocess
|
|
31
31
|
from .skill_config import load_skill_config, save_skill_config, toggle_skill, set_mode, SkillConfig
|
|
32
32
|
from .stats import DB_PATH, get_dashboard_data, get_project_details, get_skill_fire_counts, get_burn_report_data, get_api_costs
|
|
33
|
-
from .proxy.stats_tracker import
|
|
33
|
+
from .proxy.stats_tracker import (
|
|
34
|
+
get_tokenstack_stats,
|
|
35
|
+
get_lifetime_avg_savings,
|
|
36
|
+
get_code_read_savings,
|
|
37
|
+
)
|
|
34
38
|
|
|
35
39
|
_HTML_PATH = Path(__file__).parent / "dashboard.html"
|
|
36
40
|
_LAST_WORKDIR_FILE = Path.home() / ".memstack" / "agent-runner" / "last-workdir.txt"
|
|
@@ -795,6 +799,10 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
795
799
|
rng = "all"
|
|
796
800
|
try:
|
|
797
801
|
data = get_tokenstack_stats(rng)
|
|
802
|
+
# All-time code-read (AST-fired) p95 capability stat. Merged here
|
|
803
|
+
# rather than inside the range-scoped aggregate so it stays a
|
|
804
|
+
# stable claim independent of the selected time window.
|
|
805
|
+
data.update(get_code_read_savings())
|
|
798
806
|
except Exception as exc:
|
|
799
807
|
data = {"error": str(exc)}
|
|
800
808
|
body = json.dumps(data).encode()
|
{memstack_skill_loader-4.5.1 → memstack_skill_loader-4.7.0}/src/memstack_skill_loader/license.py
RENAMED
|
@@ -9,6 +9,7 @@ import hmac as hmac_mod
|
|
|
9
9
|
import json
|
|
10
10
|
import math
|
|
11
11
|
import os
|
|
12
|
+
import platform
|
|
12
13
|
import socket
|
|
13
14
|
import sys
|
|
14
15
|
import threading
|
|
@@ -406,6 +407,50 @@ def save_license_key(key: str) -> None:
|
|
|
406
407
|
# ---------------------------------------------------------------------------
|
|
407
408
|
|
|
408
409
|
|
|
410
|
+
def _environment_info() -> dict[str, str | None]:
|
|
411
|
+
"""Coarse environment info attached to every license-validation POST.
|
|
412
|
+
|
|
413
|
+
Purpose: let us see which OS / Python / loader versions our users run, so a
|
|
414
|
+
bug that silently breaks one platform (e.g. the hooks bug that killed skill
|
|
415
|
+
auto-loading on Linux/macOS) is visible in aggregate.
|
|
416
|
+
|
|
417
|
+
Collects ONLY three fields: OS name, running Python version, installed
|
|
418
|
+
loader version. It deliberately sends NO hostname, username, file path,
|
|
419
|
+
project name, or any identifier beyond the machine_id already in the
|
|
420
|
+
payload. Each field is detected independently and fail-safe: any failure
|
|
421
|
+
yields ``None`` (JSON null), never an exception — environment detection
|
|
422
|
+
must NEVER be able to fail a license validation.
|
|
423
|
+
|
|
424
|
+
``plugin_version`` is intentionally absent. The Claude Code skills-bundle
|
|
425
|
+
plugin is a separate marketplace artifact; its version lives in
|
|
426
|
+
``.claude-plugin/plugin.json`` inside Claude Code's plugin cache. This
|
|
427
|
+
loader's MCP server is registered standalone
|
|
428
|
+
(``claude mcp add ... python -m memstack_skill_loader``), so the process
|
|
429
|
+
never receives ``CLAUDE_PLUGIN_ROOT`` or any handle to that cache, and the
|
|
430
|
+
two artifacts share no version coupling. There is no reliable value to
|
|
431
|
+
send, so the field is omitted rather than guessed.
|
|
432
|
+
"""
|
|
433
|
+
info: dict[str, str | None] = {"os": None, "python_version": None, "loader_version": None}
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
info["os"] = platform.system() or None
|
|
437
|
+
except Exception:
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
info["python_version"] = platform.python_version()
|
|
442
|
+
except Exception:
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
try:
|
|
446
|
+
from importlib.metadata import version as _pkg_version
|
|
447
|
+
info["loader_version"] = _pkg_version("memstack-skill-loader")
|
|
448
|
+
except Exception:
|
|
449
|
+
pass
|
|
450
|
+
|
|
451
|
+
return info
|
|
452
|
+
|
|
453
|
+
|
|
409
454
|
async def validate_license(
|
|
410
455
|
license_key: str | None = None,
|
|
411
456
|
email: str | None = None,
|
|
@@ -644,9 +689,10 @@ async def _validate_locked(key: str, email: str | None) -> LicenseStatus:
|
|
|
644
689
|
return cached_status
|
|
645
690
|
|
|
646
691
|
# --- build request body ---
|
|
647
|
-
body_payload: dict[str, str] = {"license_key": key, "machine_id": _machine_id()}
|
|
692
|
+
body_payload: dict[str, str | None] = {"license_key": key, "machine_id": _machine_id()}
|
|
648
693
|
if email:
|
|
649
694
|
body_payload["email"] = email
|
|
695
|
+
body_payload.update(_environment_info()) # os / python_version / loader_version (fail-safe)
|
|
650
696
|
|
|
651
697
|
# --- always call API on session start ---
|
|
652
698
|
try:
|
|
@@ -710,7 +756,8 @@ def refresh_license_cache(key: str, timeout: float = HTTP_TIMEOUT) -> LicenseSta
|
|
|
710
756
|
Returns the new LicenseStatus on success, or None if the server is unreachable
|
|
711
757
|
or returns an error. All network exceptions are caught internally.
|
|
712
758
|
"""
|
|
713
|
-
body_payload: dict[str, str] = {"license_key": key, "machine_id": _machine_id()}
|
|
759
|
+
body_payload: dict[str, str | None] = {"license_key": key, "machine_id": _machine_id()}
|
|
760
|
+
body_payload.update(_environment_info()) # os / python_version / loader_version (fail-safe)
|
|
714
761
|
# Uses VALIDATE_URL directly via httpx — not routed through ANTHROPIC_BASE_URL (Anthropic SDK only).
|
|
715
762
|
try:
|
|
716
763
|
with httpx.Client(timeout=timeout) as client:
|
|
@@ -154,8 +154,31 @@ def parse_and_compress(body_dict: dict, tier: str = "free", force_pro: bool = Fa
|
|
|
154
154
|
_diag.body_transform("dedup", dedup_saved,
|
|
155
155
|
note="(cross-message repeated-block removal)")
|
|
156
156
|
|
|
157
|
+
# Body-level transforms (system-prompt compression + conversation dedup) remove
|
|
158
|
+
# tokens from content that is already inside total_compressed (the post-block
|
|
159
|
+
# forwarded text), but they run *after* total_compressed was tallied in the
|
|
160
|
+
# message loop. Credit their savings in savings_pct by computing it against the
|
|
161
|
+
# reduced "forwarded" size — but DO NOT mutate total_compressed itself: the
|
|
162
|
+
# stored compressed_tokens must stay a BLOCK-ONLY base so historical (backfilled)
|
|
163
|
+
# and new rows share exactly one convention. The read path (get_tokenstack_stats
|
|
164
|
+
# / get_proxy_stats) reconstructs the body-inclusive tokens-saved total from this
|
|
165
|
+
# same block-only base plus the dedup/system columns, so the headline % and the
|
|
166
|
+
# absolute token count stay consistent. The removals are disjoint from the
|
|
167
|
+
# block-transform deltas (sequential) and from each other, so no double-count;
|
|
168
|
+
# clamp >=0 for the rare sub-threshold case. total_original stays block-scope —
|
|
169
|
+
# folding the system prompt's original size into the denominator is
|
|
170
|
+
# unreproducible by the column-only backfill (sys_orig is not stored) and would
|
|
171
|
+
# dilute the headline. See scripts/verify_metric_fix.py for the live-data proof.
|
|
172
|
+
body_saved = 0
|
|
173
|
+
if pro:
|
|
174
|
+
if sys_t is not None and sys_t.enabled:
|
|
175
|
+
body_saved += savings.get(sys_t.stat_key, 0)
|
|
176
|
+
if dedup_t is not None and dedup_t.enabled:
|
|
177
|
+
body_saved += savings.get(dedup_t.stat_key, 0)
|
|
178
|
+
forwarded = max(0, total_compressed - body_saved)
|
|
179
|
+
|
|
157
180
|
if total_original > 0:
|
|
158
|
-
savings_pct = round((1 -
|
|
181
|
+
savings_pct = round((1 - forwarded / total_original) * 100, 1)
|
|
159
182
|
else:
|
|
160
183
|
savings_pct = 0.0
|
|
161
184
|
|