memstack-skill-loader 4.14.0__tar.gz → 4.15.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.15.0/PKG-INFO +276 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/README.md +54 -26
- memstack_skill_loader-4.15.0/pyproject.toml +51 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/__init__.py +1 -1
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/agent_runner.py +69 -3
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/categories.py +0 -1
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/config.py +19 -5
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/dashboard.html +150 -6
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/dashboard.py +121 -8
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/diary_ingest.py +42 -2
- memstack_skill_loader-4.15.0/src/memstack_skill_loader/hooks/deny_git.py +107 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/hooks/deny_manager_write.py +20 -2
- memstack_skill_loader-4.15.0/src/memstack_skill_loader/hooks/deny_reviewer_git.py +49 -0
- memstack_skill_loader-4.15.0/src/memstack_skill_loader/hooks/git_policy.py +333 -0
- memstack_skill_loader-4.15.0/src/memstack_skill_loader/index_meta.py +487 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/indexer.py +26 -2
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/server.py +123 -17
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/tfidf_search.py +48 -5
- memstack_skill_loader-4.15.0/src/memstack_skill_loader.egg-info/PKG-INFO +276 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader.egg-info/SOURCES.txt +8 -1
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader.egg-info/requires.txt +4 -1
- memstack_skill_loader-4.15.0/tests/test_dashscan.py +242 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_diary_ingest.py +76 -0
- memstack_skill_loader-4.15.0/tests/test_git_policy.py +351 -0
- memstack_skill_loader-4.15.0/tests/test_index_meta_guard.py +262 -0
- memstack_skill_loader-4.15.0/tests/test_wheel_contents.py +164 -0
- memstack_skill_loader-4.14.0/PKG-INFO +0 -11
- memstack_skill_loader-4.14.0/pyproject.toml +0 -26
- memstack_skill_loader-4.14.0/src/memstack_skill_loader/hooks/deny_git.py +0 -21
- memstack_skill_loader-4.14.0/src/memstack_skill_loader.egg-info/PKG-INFO +0 -11
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/MANIFEST.in +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/setup.cfg +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/__main__.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/compression.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/goal.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/goal_engine.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/hooks/__init__.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/license.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/memory_db.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/memory_engine.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/__init__.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/_diag.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/body_parser.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/compressor.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/forwarder.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/pro_compressor.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/server.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/proxy/stats_tracker.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/search.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/session_context.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/skill_config.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/skill_descriptions.json +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/stats.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader/version_check.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/src/memstack_skill_loader.egg-info/top_level.txt +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_advisor_strategy.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_body_parser_metric.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_builder_scope.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_code_read_savings.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_failed_approaches.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_license_environment.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_license_grace.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_memory_engine.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_memory_inspector.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_memory_scoring.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_memory_tools.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_pro_compressor.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_pro_skills_update.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_project_dir_canonical.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_search_memory.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_session_context.py +0 -0
- {memstack_skill_loader-4.14.0 → memstack_skill_loader-4.15.0}/tests/test_skill_drift.py +0 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memstack-skill-loader
|
|
3
|
+
Version: 4.15.0
|
|
4
|
+
Summary: MCP server that vector-indexes MemStack Pro skills for on-demand loading
|
|
5
|
+
Project-URL: Homepage, https://memstack.pro
|
|
6
|
+
Project-URL: Documentation, https://memstack.pro/docs
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: mcp<2.0.0,>=1.0.0
|
|
10
|
+
Requires-Dist: lancedb>=0.6.0
|
|
11
|
+
Requires-Dist: sentence-transformers>=2.2.0
|
|
12
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
13
|
+
Requires-Dist: httpx>=0.24.0
|
|
14
|
+
Requires-Dist: aiohttp>=3.9
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: setuptools>=68; extra == "dev"
|
|
17
|
+
|
|
18
|
+
# MemStack™ Skill Loader
|
|
19
|
+
|
|
20
|
+
**130 skills for Claude Code:** 86 free + 44 Pro exclusive. Vector-indexed so CC loads only the skill it needs, saving your context window.
|
|
21
|
+
|
|
22
|
+
## Quick Start (5 minutes)
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
|
|
26
|
+
**Step 1 — Install the free skills · (in Claude Code):**
|
|
27
|
+
```
|
|
28
|
+
/plugin marketplace add cwinvestments/memstack
|
|
29
|
+
/plugin install memstack@cwinvestments-memstack
|
|
30
|
+
```
|
|
31
|
+
Run both commands. This unlocks the 86 free skills right away.
|
|
32
|
+
|
|
33
|
+
> **SSH error?** ("Host key verification failed" on a fresh server that's never used GitHub over SSH.)
|
|
34
|
+
> **Default fix · (in terminal)** — rewrite GitHub to HTTPS, then retry Step 1:
|
|
35
|
+
> ```bash
|
|
36
|
+
> git config --global url."https://github.com/".insteadOf "git@github.com:"
|
|
37
|
+
> ```
|
|
38
|
+
> **Backup fix · (in terminal)** — add GitHub's host key, then retry Step 1:
|
|
39
|
+
> ```bash
|
|
40
|
+
> mkdir -p ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
|
|
41
|
+
> ```
|
|
42
|
+
|
|
43
|
+
**Step 2 — Install the engine · (in terminal):**
|
|
44
|
+
|
|
45
|
+
Windows:
|
|
46
|
+
```bash
|
|
47
|
+
pip install memstack-skill-loader
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Linux / Mac — use the explicit interpreter (the same one you register in Step 3):
|
|
51
|
+
```bash
|
|
52
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
53
|
+
```
|
|
54
|
+
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).
|
|
55
|
+
|
|
56
|
+
**Step 3 — Register the MCP server · (in terminal):** register against the **same interpreter you installed onto in Step 2.**
|
|
57
|
+
|
|
58
|
+
Windows:
|
|
59
|
+
```bash
|
|
60
|
+
claude mcp add --scope user memstack-skills -- python -m memstack_skill_loader
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Linux / Mac:
|
|
64
|
+
```bash
|
|
65
|
+
claude mcp add --scope user memstack-skills -- /usr/bin/python3 -m memstack_skill_loader
|
|
66
|
+
```
|
|
67
|
+
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).
|
|
68
|
+
|
|
69
|
+
**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:
|
|
70
|
+
```
|
|
71
|
+
activate_license(key="your-key", email="you@example.com")
|
|
72
|
+
```
|
|
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
|
+
|
|
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.
|
|
76
|
+
|
|
77
|
+
> To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
|
|
78
|
+
|
|
79
|
+
> See [Getting Started](https://memstack.pro/docs/getting-started) for detailed setup and [Skill Reference](https://memstack.pro/docs/skills) for the full skill catalog. If you hit issues, the [Troubleshooting](#troubleshooting) section below covers the common ones.
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
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.
|
|
84
|
+
|
|
85
|
+
**`error: externally-managed-environment` (during Step 2)** — pip refuses to install on a system-managed Python. Add `--break-system-packages` · (in terminal):
|
|
86
|
+
```bash
|
|
87
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**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.
|
|
91
|
+
|
|
92
|
+
**`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):
|
|
93
|
+
```bash
|
|
94
|
+
/usr/bin/python3 -c "import memstack_skill_loader; print('ok')"
|
|
95
|
+
```
|
|
96
|
+
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):
|
|
97
|
+
```bash
|
|
98
|
+
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
99
|
+
claude mcp add --scope user memstack-skills -- /usr/bin/python3 -m memstack_skill_loader
|
|
100
|
+
```
|
|
101
|
+
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.
|
|
102
|
+
|
|
103
|
+
**`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):
|
|
104
|
+
```bash
|
|
105
|
+
sudo apt install python3-pip
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How It Works
|
|
109
|
+
|
|
110
|
+
MCP server that vector-indexes all 130 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.
|
|
111
|
+
|
|
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
|
+
- **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
|
|
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
|
+
- **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
|
+
- **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
|
|
118
|
+
|
|
119
|
+
### Data and privacy
|
|
120
|
+
|
|
121
|
+
Outbound calls fall into two groups: the ones the loader makes on its own, and the ones you trigger. Both are listed in full.
|
|
122
|
+
|
|
123
|
+
**Automatic — these happen at session start without you asking:**
|
|
124
|
+
|
|
125
|
+
- **License validation** → POST to `admin.cwaffiliateinvestments.com/api/licenses/validate`, with:
|
|
126
|
+
- your **license key**
|
|
127
|
+
- a **machine ID** — a stable, hashed fingerprint that binds the license to your machine
|
|
128
|
+
- your **email**, if you provided one at activation
|
|
129
|
+
- your **environment** — OS name, Python version, and MemStack loader version
|
|
130
|
+
|
|
131
|
+
That is the complete payload.
|
|
132
|
+
- **Pro bundle version probe** → GET to `admin.cwaffiliateinvestments.com/api/skills/pro-bundle/version`. Pro licenses only, at most once every 24 hours. Sends your license key as a bearer token and nothing else; it asks only whether a newer skill bundle exists.
|
|
133
|
+
- **Update check** → GET to `pypi.org/pypi/memstack-skill-loader/json`. At most once every 24 hours. Sends no payload at all — it reads the latest published version number so the loader can tell you an upgrade is available.
|
|
134
|
+
|
|
135
|
+
**Triggered by you — these never fire on their own:**
|
|
136
|
+
|
|
137
|
+
- **Pro skills download** → GET to `admin.cwaffiliateinvestments.com/api/skills/pro-bundle`, when you run `activate_license` or `refresh_pro_skills`, and when the version probe above finds a newer bundle. Sends your license key.
|
|
138
|
+
- **Agent Runner** → `api.anthropic.com`, only while you are running an agent. This is the one call that carries your actual work: the task you wrote and the contents of the files the agents read. It uses your own Anthropic API key, and nothing is sent unless you start an agent.
|
|
139
|
+
- **Referrals page** → POST to `memstack.pro/api/referrals/me`, only when you open that dashboard page. Sends your license key.
|
|
140
|
+
- **TokenStack proxy** → `api.anthropic.com`, only if you start the proxy yourself. It forwards your Claude Code traffic after compressing it, and is off unless you turn it on.
|
|
141
|
+
|
|
142
|
+
Outside the Agent Runner and the TokenStack proxy — both of which you start deliberately, and both of which talk to Anthropic rather than to us — **no code, file contents, file paths, usernames, project names, or prompts are ever sent.** The environment fields in license validation exist only so platform-specific breakage (for example, a bug that affects only Linux) is visible to us in aggregate.
|
|
143
|
+
|
|
144
|
+
### Environment Variable Override
|
|
145
|
+
|
|
146
|
+
Set `MEMSTACK_SKILLS_DIR` to override the skills path in `config.json`:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
export MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Stack
|
|
153
|
+
|
|
154
|
+
- Python 3.10+
|
|
155
|
+
- [MCP SDK](https://pypi.org/project/mcp/) (stdio transport)
|
|
156
|
+
- [LanceDB](https://lancedb.com/) (vector storage, zero external dependencies)
|
|
157
|
+
- [sentence-transformers](https://www.sbert.net/) with `all-MiniLM-L6-v2` (384-dim local embeddings)
|
|
158
|
+
|
|
159
|
+
## Available Tools
|
|
160
|
+
|
|
161
|
+
### `find_skill`
|
|
162
|
+
Search skills by describing what you need. Returns the most relevant skill(s) with full instructions.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
find_skill(query="deploy to Railway", top_k=3)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `list_skills`
|
|
169
|
+
Browse the full skill catalog with names and descriptions.
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
list_skills()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `get_skill`
|
|
176
|
+
Load a specific skill by exact name.
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
get_skill(name="railway deploy")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `reindex_skills`
|
|
183
|
+
Rebuild the vector index after adding or modifying skills.
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
reindex_skills()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Memory Engine
|
|
190
|
+
|
|
191
|
+
The `mem_*` tools back a persistent, append-only fact store shared across sessions (see [How It Works](#how-it-works)).
|
|
192
|
+
|
|
193
|
+
- **`mem_store`** — Store a new immutable fact (subject, claim, provenance). Returns the fact id and its provenance tag.
|
|
194
|
+
- **`mem_correct`** — Correct a fact by superseding it with a new one. Corrections extend only from the live tip of a chain; correcting a superseded fact is refused and names the current tip.
|
|
195
|
+
- **`mem_recall`** — Recall live facts for a project, each rendered with an inline provenance tag `[method · source · recalled Nx]`. Each returned fact is logged as a recall. Optional full-text query or exact-subject filter.
|
|
196
|
+
- **`mem_inspect`** — Inspect a single fact and its full supersession chain (root → tip) with statuses, provenance, entities, and recall count.
|
|
197
|
+
- **`mem_stats`** — Fact-store statistics: totals by status (live/superseded/retracted), distinct subjects, and recall count. Optionally scoped to a project.
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
The `config.json` file controls where skills are loaded from:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"skill_sources": [
|
|
206
|
+
{
|
|
207
|
+
"type": "local",
|
|
208
|
+
"path": "/path/to/memstack/skills",
|
|
209
|
+
"pattern": "**/SKILL.md",
|
|
210
|
+
"label": "MemStack"
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
"embedding_model": "all-MiniLM-L6-v2",
|
|
214
|
+
"default_top_k": 3,
|
|
215
|
+
"vector_db_path": "./vectors",
|
|
216
|
+
"auto_reindex_on_start": true
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`vector_db_path` sets where the skill index is stored. A relative value resolves against the canonical state root `~/.memstack`, not against the directory holding `config.json`, so the index lives in one place regardless of which directory a process is started from. An absolute or `~`-prefixed value is honored verbatim.
|
|
221
|
+
|
|
222
|
+
You never add Pro skills to `config.json` by hand: the loader adds that directory itself once a key is present. Run `activate_license` at least once regardless, because that is the step that validates your key and downloads the Pro skills. `MEMSTACK_PRO_LICENSE_KEY` is an optional override that supplies the key without storing it, and it takes precedence over the stored one, but on its own it downloads nothing.
|
|
223
|
+
|
|
224
|
+
Add entries to `skill_sources` to index skills from multiple directories:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"skill_sources": [
|
|
229
|
+
{
|
|
230
|
+
"type": "local",
|
|
231
|
+
"path": "/path/to/memstack/skills",
|
|
232
|
+
"pattern": "**/SKILL.md",
|
|
233
|
+
"label": "MemStack"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"type": "local",
|
|
237
|
+
"path": "/home/user/custom-skills",
|
|
238
|
+
"pattern": "*.md",
|
|
239
|
+
"label": "My Custom Skills"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The `pattern` field controls how skills are discovered:
|
|
246
|
+
- `**/SKILL.md`: Subdirectory structure (e.g., `category/skill-name/SKILL.md`)
|
|
247
|
+
- `*.md`: Flat directory (each `.md` file is a skill)
|
|
248
|
+
|
|
249
|
+
## Release Notes
|
|
250
|
+
|
|
251
|
+
### 4.15.0 (August 2026)
|
|
252
|
+
|
|
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.
|
|
254
|
+
- 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
|
+
- Agent Runner git policy rewritten as an argv-level classifier, replacing a substring match that could not tell `git status` from `git push`.
|
|
256
|
+
|
|
257
|
+
### 4.10.0 to 4.14.1 (July 2026)
|
|
258
|
+
|
|
259
|
+
- Memory Engine: diary `## FACTS` ingestion, living-memory injection at session start, a read-only Memory Inspector dashboard page, and recall that ranks the whole live fact set rather than a window of it.
|
|
260
|
+
- Added the goal skill, bringing the public catalog to 130 skills (86 free + 44 Pro).
|
|
261
|
+
- Added the video-review Pro skill.
|
|
262
|
+
- Referrals page added to the dashboard.
|
|
263
|
+
- License validation now discloses its full payload in-product (see [Data and privacy](#data-and-privacy)).
|
|
264
|
+
- Packaging fix: `skill_descriptions.json` now ships in the wheel, so a pip install no longer falls back to empty dashboard descriptions.
|
|
265
|
+
|
|
266
|
+
### 4.0.0 to 4.9.0 (May to July 2026)
|
|
267
|
+
|
|
268
|
+
- Dashboard and Agent Runner: 3-agent orchestration (Manager, Builder, Reviewer) with per-agent model selection, context window monitoring, and token cost tracking.
|
|
269
|
+
- Session diary with markdown narratives, and safe git staging that keeps secrets and runtime data out of commits.
|
|
270
|
+
- TokenStack proxy compression, including AST compression of line-numbered Python.
|
|
271
|
+
- Memory Browser with project filtering and secret masking.
|
|
272
|
+
- Licensing hardened against transient network failures and machine mismatches.
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
Proprietary. Part of MemStack™ Pro by CW Affiliate Investments LLC.
|
|
@@ -55,12 +55,16 @@ 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.
|
|
59
|
+
|
|
58
60
|
> To override the skills path, set `MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills`.
|
|
59
61
|
|
|
60
|
-
> See [
|
|
62
|
+
> See [Getting Started](https://memstack.pro/docs/getting-started) for detailed setup and [Skill Reference](https://memstack.pro/docs/skills) for the full skill catalog. If you hit issues, the [Troubleshooting](#troubleshooting) section below covers the common ones.
|
|
61
63
|
|
|
62
64
|
## Troubleshooting
|
|
63
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.
|
|
67
|
+
|
|
64
68
|
**`error: externally-managed-environment` (during Step 2)** — pip refuses to install on a system-managed Python. Add `--break-system-packages` · (in terminal):
|
|
65
69
|
```bash
|
|
66
70
|
/usr/bin/python3 -m pip install memstack-skill-loader --break-system-packages
|
|
@@ -88,23 +92,37 @@ sudo apt install python3-pip
|
|
|
88
92
|
|
|
89
93
|
MCP server that vector-indexes all 130 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
94
|
|
|
91
|
-
- **No Anthropic API key required:** skill search and indexing run locally
|
|
92
|
-
- **Pro skills auto-detected:**
|
|
93
|
-
- **Auto-reindex on start:** skills stay current without manual rebuilds
|
|
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
|
+
- **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
|
|
94
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))
|
|
95
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
|
|
96
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
|
|
97
101
|
|
|
98
102
|
### Data and privacy
|
|
99
103
|
|
|
100
|
-
|
|
104
|
+
Outbound calls fall into two groups: the ones the loader makes on its own, and the ones you trigger. Both are listed in full.
|
|
105
|
+
|
|
106
|
+
**Automatic — these happen at session start without you asking:**
|
|
107
|
+
|
|
108
|
+
- **License validation** → POST to `admin.cwaffiliateinvestments.com/api/licenses/validate`, with:
|
|
109
|
+
- your **license key**
|
|
110
|
+
- a **machine ID** — a stable, hashed fingerprint that binds the license to your machine
|
|
111
|
+
- your **email**, if you provided one at activation
|
|
112
|
+
- your **environment** — OS name, Python version, and MemStack loader version
|
|
113
|
+
|
|
114
|
+
That is the complete payload.
|
|
115
|
+
- **Pro bundle version probe** → GET to `admin.cwaffiliateinvestments.com/api/skills/pro-bundle/version`. Pro licenses only, at most once every 24 hours. Sends your license key as a bearer token and nothing else; it asks only whether a newer skill bundle exists.
|
|
116
|
+
- **Update check** → GET to `pypi.org/pypi/memstack-skill-loader/json`. At most once every 24 hours. Sends no payload at all — it reads the latest published version number so the loader can tell you an upgrade is available.
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
- a **machine ID** — a stable, hashed fingerprint that binds the license to your machine
|
|
104
|
-
- your **email**, if you provided one at activation
|
|
105
|
-
- your **environment** — OS name, Python version, and MemStack loader version
|
|
118
|
+
**Triggered by you — these never fire on their own:**
|
|
106
119
|
|
|
107
|
-
|
|
120
|
+
- **Pro skills download** → GET to `admin.cwaffiliateinvestments.com/api/skills/pro-bundle`, when you run `activate_license` or `refresh_pro_skills`, and when the version probe above finds a newer bundle. Sends your license key.
|
|
121
|
+
- **Agent Runner** → `api.anthropic.com`, only while you are running an agent. This is the one call that carries your actual work: the task you wrote and the contents of the files the agents read. It uses your own Anthropic API key, and nothing is sent unless you start an agent.
|
|
122
|
+
- **Referrals page** → POST to `memstack.pro/api/referrals/me`, only when you open that dashboard page. Sends your license key.
|
|
123
|
+
- **TokenStack proxy** → `api.anthropic.com`, only if you start the proxy yourself. It forwards your Claude Code traffic after compressing it, and is off unless you turn it on.
|
|
124
|
+
|
|
125
|
+
Outside the Agent Runner and the TokenStack proxy — both of which you start deliberately, and both of which talk to Anthropic rather than to us — **no code, file contents, file paths, usernames, project names, or prompts are ever sent.** The environment fields in license validation exist only so platform-specific breakage (for example, a bug that affects only Linux) is visible to us in aggregate.
|
|
108
126
|
|
|
109
127
|
### Environment Variable Override
|
|
110
128
|
|
|
@@ -116,7 +134,7 @@ export MEMSTACK_SKILLS_DIR=/path/to/your/memstack/skills
|
|
|
116
134
|
|
|
117
135
|
## Stack
|
|
118
136
|
|
|
119
|
-
- Python 3.
|
|
137
|
+
- Python 3.10+
|
|
120
138
|
- [MCP SDK](https://pypi.org/project/mcp/) (stdio transport)
|
|
121
139
|
- [LanceDB](https://lancedb.com/) (vector storage, zero external dependencies)
|
|
122
140
|
- [sentence-transformers](https://www.sbert.net/) with `all-MiniLM-L6-v2` (384-dim local embeddings)
|
|
@@ -170,7 +188,7 @@ The `config.json` file controls where skills are loaded from:
|
|
|
170
188
|
"skill_sources": [
|
|
171
189
|
{
|
|
172
190
|
"type": "local",
|
|
173
|
-
"path": "
|
|
191
|
+
"path": "/path/to/memstack/skills",
|
|
174
192
|
"pattern": "**/SKILL.md",
|
|
175
193
|
"label": "MemStack"
|
|
176
194
|
}
|
|
@@ -182,7 +200,9 @@ The `config.json` file controls where skills are loaded from:
|
|
|
182
200
|
}
|
|
183
201
|
```
|
|
184
202
|
|
|
185
|
-
|
|
203
|
+
`vector_db_path` sets where the skill index is stored. A relative value resolves against the canonical state root `~/.memstack`, not against the directory holding `config.json`, so the index lives in one place regardless of which directory a process is started from. An absolute or `~`-prefixed value is honored verbatim.
|
|
204
|
+
|
|
205
|
+
You never add Pro skills to `config.json` by hand: the loader adds that directory itself once a key is present. Run `activate_license` at least once regardless, because that is the step that validates your key and downloads the Pro skills. `MEMSTACK_PRO_LICENSE_KEY` is an optional override that supplies the key without storing it, and it takes precedence over the stored one, but on its own it downloads nothing.
|
|
186
206
|
|
|
187
207
|
Add entries to `skill_sources` to index skills from multiple directories:
|
|
188
208
|
|
|
@@ -191,7 +211,7 @@ Add entries to `skill_sources` to index skills from multiple directories:
|
|
|
191
211
|
"skill_sources": [
|
|
192
212
|
{
|
|
193
213
|
"type": "local",
|
|
194
|
-
"path": "
|
|
214
|
+
"path": "/path/to/memstack/skills",
|
|
195
215
|
"pattern": "**/SKILL.md",
|
|
196
216
|
"label": "MemStack"
|
|
197
217
|
},
|
|
@@ -211,20 +231,28 @@ The `pattern` field controls how skills are discovered:
|
|
|
211
231
|
|
|
212
232
|
## Release Notes
|
|
213
233
|
|
|
214
|
-
###
|
|
234
|
+
### 4.15.0 (August 2026)
|
|
235
|
+
|
|
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.
|
|
237
|
+
- 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
|
+
- Agent Runner git policy rewritten as an argv-level classifier, replacing a substring match that could not tell `git status` from `git push`.
|
|
239
|
+
|
|
240
|
+
### 4.10.0 to 4.14.1 (July 2026)
|
|
241
|
+
|
|
242
|
+
- Memory Engine: diary `## FACTS` ingestion, living-memory injection at session start, a read-only Memory Inspector dashboard page, and recall that ranks the whole live fact set rather than a window of it.
|
|
243
|
+
- Added the goal skill, bringing the public catalog to 130 skills (86 free + 44 Pro).
|
|
244
|
+
- Added the video-review Pro skill.
|
|
245
|
+
- Referrals page added to the dashboard.
|
|
246
|
+
- License validation now discloses its full payload in-product (see [Data and privacy](#data-and-privacy)).
|
|
247
|
+
- Packaging fix: `skill_descriptions.json` now ships in the wheel, so a pip install no longer falls back to empty dashboard descriptions.
|
|
215
248
|
|
|
216
|
-
|
|
217
|
-
- Agent Runner: 3-agent orchestration (Manager/Builder/Reviewer) with per-agent model selection
|
|
218
|
-
- 17 MCP tools
|
|
219
|
-
- 127 skills (84 free + 43 Pro)
|
|
220
|
-
- Real-time context window monitoring per agent
|
|
221
|
-
- Session diary with AI-authored markdown narratives
|
|
222
|
-
- Safe git staging (prevents accidental commits of secrets/runtime data)
|
|
223
|
-
- Task completion notifications (browser, tab flash, audio)
|
|
224
|
-
- Token usage tracking with estimated costs
|
|
225
|
-
- TokenStack™ proxy integration (~35-40% token savings)
|
|
249
|
+
### 4.0.0 to 4.9.0 (May to July 2026)
|
|
226
250
|
|
|
227
|
-
|
|
251
|
+
- Dashboard and Agent Runner: 3-agent orchestration (Manager, Builder, Reviewer) with per-agent model selection, context window monitoring, and token cost tracking.
|
|
252
|
+
- Session diary with markdown narratives, and safe git staging that keeps secrets and runtime data out of commits.
|
|
253
|
+
- TokenStack proxy compression, including AST compression of line-numbered Python.
|
|
254
|
+
- Memory Browser with project filtering and secret masking.
|
|
255
|
+
- Licensing hardened against transient network failures and machine mismatches.
|
|
228
256
|
|
|
229
257
|
## License
|
|
230
258
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "memstack-skill-loader"
|
|
7
|
+
version = "4.15.0"
|
|
8
|
+
description = "MCP server that vector-indexes MemStack Pro skills for on-demand loading"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
# Upper bound is load-bearing: mcp 2.0.0 (2026-07-28) removed the
|
|
13
|
+
# Server.list_tools()/call_tool() decorator API that server.py is built on.
|
|
14
|
+
# Do not relax without porting server.py to the 2.x API first.
|
|
15
|
+
"mcp>=1.0.0,<2.0.0",
|
|
16
|
+
"lancedb>=0.6.0",
|
|
17
|
+
"sentence-transformers>=2.2.0",
|
|
18
|
+
"pyarrow>=14.0.0",
|
|
19
|
+
"httpx>=0.24.0",
|
|
20
|
+
"aiohttp>=3.9",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://memstack.pro"
|
|
25
|
+
Documentation = "https://memstack.pro/docs"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
# setuptools is a TEST dependency here, not only a build dependency. The
|
|
29
|
+
# packaging guard in tests/test_wheel_contents.py builds a wheel and inspects
|
|
30
|
+
# it, and it fails rather than skips when the backend is absent, because a skip
|
|
31
|
+
# is a silent pass. Python 3.12+ venvs no longer ship setuptools, so a bare venv
|
|
32
|
+
# needs: pip install -e ".[dev]"
|
|
33
|
+
dev = ["setuptools>=68"]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
memstack-index = "memstack_skill_loader.indexer:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
where = ["src"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.package-data]
|
|
42
|
+
memstack_skill_loader = ["dashboard.html", "skill_descriptions.json"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
# Test the WORKING TREE, not whatever is installed. This package is installed
|
|
46
|
+
# non-editable, so without this the suite imported memstack_skill_loader from
|
|
47
|
+
# site-packages and silently graded a stale copy: a source change could be
|
|
48
|
+
# green here and still be broken, and a new test could fail against code that
|
|
49
|
+
# was already fixed. pythonpath puts src/ ahead of site-packages for the whole
|
|
50
|
+
# suite, so `python -m pytest` needs no PYTHONPATH prefix to be correct.
|
|
51
|
+
pythonpath = ["src"]
|
|
@@ -24,6 +24,9 @@ from .stats import log_agent_invocation, log_api_cost
|
|
|
24
24
|
# ---------------------------------------------------------------------------
|
|
25
25
|
|
|
26
26
|
MODEL_PRICING = {
|
|
27
|
+
"claude-fable-5": {"input": 10.0, "output": 50.0},
|
|
28
|
+
"claude-opus-5": {"input": 5.0, "output": 25.0},
|
|
29
|
+
"claude-sonnet-5": {"input": 3.0, "output": 15.0},
|
|
27
30
|
"claude-sonnet-4-6": {"input": 3.0, "output": 15.0},
|
|
28
31
|
"claude-opus-4-6": {"input": 5.0, "output": 25.0},
|
|
29
32
|
"claude-opus-4-7": {"input": 5.0, "output": 25.0},
|
|
@@ -177,7 +180,15 @@ SYSTEM_PROMPTS = {
|
|
|
177
180
|
"- Never hardcode secrets, API keys, tokens, passwords, or credentials in source code. "
|
|
178
181
|
"Always use environment variables loaded from .env files. If a secret is needed, create "
|
|
179
182
|
"or update a .env.example file with the variable name and a placeholder value.\n"
|
|
180
|
-
"-
|
|
183
|
+
"- Read-only git IS available to you: status, log, diff, show, ls-files, "
|
|
184
|
+
"check-ignore, branch/remote listing, ls-remote, rev-parse. Use it to "
|
|
185
|
+
"understand the repository before you change it.\n"
|
|
186
|
+
"- Do NOT run git add, git commit, git push, git merge, git checkout, or "
|
|
187
|
+
"git reset. Committing is the orchestrator's job, not yours. These are "
|
|
188
|
+
"denied in code, not on the honour system, so do not try to work around "
|
|
189
|
+
"the denial.\n"
|
|
190
|
+
"- Issue ONE git command per Bash call. Chaining with && or ; is denied, "
|
|
191
|
+
"as are output redirection and `git -C <other-repo>`."
|
|
181
192
|
),
|
|
182
193
|
"advisor": (
|
|
183
194
|
"You are an Advisor agent. You are called in ONLY when a Builder agent has failed "
|
|
@@ -214,6 +225,11 @@ SYSTEM_PROMPTS = {
|
|
|
214
225
|
"verdict that the orchestrator parses as a hard contract.\n\n"
|
|
215
226
|
"You MUST read the actual changed files to verify correctness. Do not trust the Builder's "
|
|
216
227
|
"summary alone.\n\n"
|
|
228
|
+
"Read-only git is available to you for verification: status, log, diff, show, ls-files, "
|
|
229
|
+
"branch/remote listing, ls-remote, rev-parse, symbolic-ref. Git writes (commit, push, "
|
|
230
|
+
"merge, checkout, reset, fetch) are denied in code. Issue ONE git command per Bash call: "
|
|
231
|
+
"chaining with && or ;, output redirection, and `git -C <other-repo>` are all denied, so "
|
|
232
|
+
"run them as separate calls rather than trying to combine them.\n\n"
|
|
217
233
|
"## Review Process\n\n"
|
|
218
234
|
"Evaluate the submission across these 4 perspectives:\n"
|
|
219
235
|
"1. **SECURITY** — secret/credential exposure, injection risks, auth/permission changes, "
|
|
@@ -1062,6 +1078,19 @@ def _build_env(strip_api_key: bool = False, inject_api_key: str | None = None) -
|
|
|
1062
1078
|
env["ANTHROPIC_API_KEY"] = inject_api_key
|
|
1063
1079
|
if ANTHROPIC_BASE_URL:
|
|
1064
1080
|
env["ANTHROPIC_BASE_URL"] = ANTHROPIC_BASE_URL
|
|
1081
|
+
# Git hardening for agent subprocesses. A PreToolUse hook can only allow or
|
|
1082
|
+
# deny, never rewrite, so the vectors that live in repo-local config are
|
|
1083
|
+
# closed here instead (AGENT_GIT_CAPABILITY_DESIGN.md §1.2):
|
|
1084
|
+
# GIT_PAGER - .git/config core.pager is arbitrary code execution
|
|
1085
|
+
# GIT_EXTERNAL_DIFF - .gitattributes can point `git diff` at a program
|
|
1086
|
+
# GIT_CONFIG_NOSYSTEM- ignore system-wide config the agent could reach
|
|
1087
|
+
# GIT_TERMINAL_PROMPT- a network read against an unauthenticated remote
|
|
1088
|
+
# must error, not hang until the inactivity watchdog
|
|
1089
|
+
env["GIT_PAGER"] = "cat"
|
|
1090
|
+
env["GIT_EXTERNAL_DIFF"] = ""
|
|
1091
|
+
env["GIT_CONFIG_NOSYSTEM"] = "1"
|
|
1092
|
+
env["GIT_TERMINAL_PROMPT"] = "0"
|
|
1093
|
+
env["GIT_ASKPASS"] = ""
|
|
1065
1094
|
return env
|
|
1066
1095
|
|
|
1067
1096
|
|
|
@@ -1764,13 +1793,33 @@ def _parse_subtasks(text: str) -> list[tuple[str, str, list[str]]]:
|
|
|
1764
1793
|
|
|
1765
1794
|
|
|
1766
1795
|
def _build_builder_hook_settings() -> str:
|
|
1767
|
-
"""Inline --settings JSON that registers the Builder git
|
|
1796
|
+
"""Inline --settings JSON that registers the Builder git PreToolUse hook.
|
|
1768
1797
|
|
|
1769
1798
|
The hook script ships inside this package (hooks/deny_git.py). Its absolute
|
|
1770
1799
|
path MUST use forward slashes: backslashes get stripped when the settings
|
|
1771
1800
|
command string is executed on Windows, which mangles the path and breaks
|
|
1772
|
-
the hook (see smoke test).
|
|
1801
|
+
the hook (see smoke test).
|
|
1802
|
+
|
|
1803
|
+
The matcher covers the file-writing tools as well as Bash because the hook
|
|
1804
|
+
now also protects .gitattributes / .gitconfig / .git/** from being authored
|
|
1805
|
+
(AGENT_GIT_CAPABILITY_DESIGN.md §1.3); a Bash-only matcher would leave the
|
|
1806
|
+
Write/Edit route wide open."""
|
|
1773
1807
|
hook_script = os.path.join(os.path.dirname(__file__), "hooks", "deny_git.py").replace("\\", "/")
|
|
1808
|
+
return json.dumps({
|
|
1809
|
+
"hooks": {"PreToolUse": [{"matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash",
|
|
1810
|
+
"hooks": [{"type": "command", "command": f"python {hook_script}"}]}]}
|
|
1811
|
+
})
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
def _build_reviewer_hook_settings() -> str:
|
|
1815
|
+
"""Inline --settings JSON that registers the Reviewer read-only-git hook.
|
|
1816
|
+
|
|
1817
|
+
Mirrors the other two builders, including the forward-slash path (Windows
|
|
1818
|
+
strips backslashes out of the settings command string). The Reviewer had no
|
|
1819
|
+
PreToolUse hook at all before this; Bash is the only matcher because the
|
|
1820
|
+
Reviewer must stay free to read files, build, and test in order to verify
|
|
1821
|
+
a deploy independently."""
|
|
1822
|
+
hook_script = os.path.join(os.path.dirname(__file__), "hooks", "deny_reviewer_git.py").replace("\\", "/")
|
|
1774
1823
|
return json.dumps({
|
|
1775
1824
|
"hooks": {"PreToolUse": [{"matcher": "Bash",
|
|
1776
1825
|
"hooks": [{"type": "command", "command": f"python {hook_script}"}]}]}
|
|
@@ -2034,6 +2083,9 @@ def _orchestrate(session: Session) -> None:
|
|
|
2034
2083
|
# Manager-only read-only hook (Layer 1). Passed to the Manager
|
|
2035
2084
|
# _invoke_agent call via extra_settings; never to Builder/Reviewer.
|
|
2036
2085
|
manager_hook_settings = _build_manager_hook_settings()
|
|
2086
|
+
# Reviewer-only read-only-git hook (Layer 1). Passed to both Reviewer
|
|
2087
|
+
# _invoke_agent call sites (subtask and single-task paths).
|
|
2088
|
+
reviewer_hook_settings = _build_reviewer_hook_settings()
|
|
2037
2089
|
|
|
2038
2090
|
try:
|
|
2039
2091
|
# Load execution modes and custom display names for this run
|
|
@@ -2356,6 +2408,7 @@ def _orchestrate(session: Session) -> None:
|
|
|
2356
2408
|
model=session.models.get("reviewer", ""),
|
|
2357
2409
|
display_name=display_names["reviewer"],
|
|
2358
2410
|
execution_mode=agent_modes["reviewer"],
|
|
2411
|
+
extra_settings=reviewer_hook_settings,
|
|
2359
2412
|
)
|
|
2360
2413
|
try:
|
|
2361
2414
|
# Gap 2: strict verdict contract, fail-closed, re-prompt at most once.
|
|
@@ -2475,6 +2528,12 @@ def _orchestrate(session: Session) -> None:
|
|
|
2475
2528
|
_re.compile(r'__pycache__[\\/]'),
|
|
2476
2529
|
_re.compile(r'node_modules[\\/]'),
|
|
2477
2530
|
_re.compile(r'^\.env'),
|
|
2531
|
+
# Repository config: never auto-staged. A .gitattributes
|
|
2532
|
+
# or .git/config change can turn read-only git into code
|
|
2533
|
+
# execution (AGENT_GIT_CAPABILITY_DESIGN.md §1.3).
|
|
2534
|
+
_re.compile(r'(^|[\\/])\.gitattributes$'),
|
|
2535
|
+
_re.compile(r'(^|[\\/])\.gitconfig$'),
|
|
2536
|
+
_re.compile(r'(^|[\\/])\.git[\\/]'),
|
|
2478
2537
|
_re.compile(r'secret', _re.IGNORECASE),
|
|
2479
2538
|
_re.compile(r'credential', _re.IGNORECASE),
|
|
2480
2539
|
]
|
|
@@ -2730,6 +2789,7 @@ def _orchestrate(session: Session) -> None:
|
|
|
2730
2789
|
model=session.models.get("reviewer", ""),
|
|
2731
2790
|
display_name=display_names["reviewer"],
|
|
2732
2791
|
execution_mode=agent_modes["reviewer"],
|
|
2792
|
+
extra_settings=reviewer_hook_settings,
|
|
2733
2793
|
)
|
|
2734
2794
|
try:
|
|
2735
2795
|
# Gap 2: strict verdict contract, fail-closed, re-prompt at most once.
|
|
@@ -2793,6 +2853,12 @@ def _orchestrate(session: Session) -> None:
|
|
|
2793
2853
|
_re.compile(r'__pycache__[\\/]'),
|
|
2794
2854
|
_re.compile(r'node_modules[\\/]'),
|
|
2795
2855
|
_re.compile(r'^\.env'),
|
|
2856
|
+
# Repository config: never auto-staged. A .gitattributes
|
|
2857
|
+
# or .git/config change can turn read-only git into
|
|
2858
|
+
# code execution (AGENT_GIT_CAPABILITY_DESIGN.md §1.3).
|
|
2859
|
+
_re.compile(r'(^|[\\/])\.gitattributes$'),
|
|
2860
|
+
_re.compile(r'(^|[\\/])\.gitconfig$'),
|
|
2861
|
+
_re.compile(r'(^|[\\/])\.git[\\/]'),
|
|
2796
2862
|
_re.compile(r'secret', _re.IGNORECASE),
|
|
2797
2863
|
_re.compile(r'credential', _re.IGNORECASE),
|
|
2798
2864
|
]
|
|
@@ -18,7 +18,6 @@ CATEGORY_MAP = {
|
|
|
18
18
|
"humanize": "Content", "blog-post": "Content", "email-sequence": "Content",
|
|
19
19
|
"landing-page-copy": "Content", "newsletter": "Content", "product-description": "Content",
|
|
20
20
|
"tiktok-script": "Content", "twitter-thread": "Content", "youtube-script": "Content",
|
|
21
|
-
"kdp-format": "Content",
|
|
22
21
|
# Deployment
|
|
23
22
|
"railway-deploy": "Deployment", "docker-setup": "Deployment", "netlify-deploy": "Deployment",
|
|
24
23
|
"domain-ssl": "Deployment", "hetzner-setup": "Deployment", "ci-cd-pipeline": "Deployment",
|