mcp-memory-keeper 0.11.0 → 0.12.1
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.
- package/CHANGELOG.md +47 -1
- package/README.md +119 -1
- package/dist/__tests__/integration/git-integration.test.js +4 -1
- package/dist/__tests__/integration/project-directory.test.js +6 -0
- package/dist/__tests__/integration/server-initialization.test.js +4 -6
- package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +3 -0
- package/dist/__tests__/utils/tool-profiles.test.js +374 -0
- package/dist/index.js +1095 -1053
- package/dist/utils/tool-profiles.js +242 -0
- package/examples/config.json +31 -0
- package/examples/project-directory-setup.md +114 -0
- package/package.json +2 -1
- package/dist/__tests__/integration/issue-token-limit-channel-query.test.js +0 -128
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.1] - 2026-03-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Server crashes with SQLITE_CANTOPEN when parent process CWD changes** (#31)
|
|
15
|
+
- Server now resolves database path to an absolute location (`$DATA_DIR` or `~/mcp-data/memory-keeper/`) instead of relying on CWD
|
|
16
|
+
- Added try/catch around data directory creation with actionable error message
|
|
17
|
+
- Startup warning with exact `cp` command when legacy `context.db` detected in CWD
|
|
18
|
+
- README "from source" install command now points to `bin/mcp-memory-keeper` instead of `node dist/index.js`
|
|
19
|
+
- Added Upgrading section documenting database path change and migration steps
|
|
20
|
+
|
|
21
|
+
### Technical
|
|
22
|
+
|
|
23
|
+
- Fixed integration tests to use `DATA_DIR` instead of dead `MCP_DB_PATH` environment variable
|
|
24
|
+
- Fixed `git.init()` in tests to use `--initial-branch=master` for deterministic behavior
|
|
25
|
+
|
|
26
|
+
## [0.12.0] - 2026-02-06
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **Selective Tool Filtering via Profiles** (#29)
|
|
31
|
+
- Control which tools are exposed to reduce context window usage (~10-15K tokens saved with minimal profile)
|
|
32
|
+
- Three built-in profiles: `minimal` (8 tools), `standard` (22 tools), `full` (38 tools, default)
|
|
33
|
+
- `TOOL_PROFILE` environment variable to select active profile at startup
|
|
34
|
+
- `TOOL_PROFILE_CONFIG` environment variable to specify custom config file path
|
|
35
|
+
- Custom profile definitions via `~/.mcp-memory-keeper/config.json`
|
|
36
|
+
- Config file profiles take precedence over built-in defaults
|
|
37
|
+
- Helpful error messages when disabled tools are called, with guidance on enabling them
|
|
38
|
+
- Startup logging shows active profile, tool count, and source
|
|
39
|
+
- Example config file included in `examples/config.json`
|
|
40
|
+
|
|
41
|
+
### Technical
|
|
42
|
+
|
|
43
|
+
- New `src/utils/tool-profiles.ts` module with `ALL_TOOL_NAMES` source of truth
|
|
44
|
+
- `ToolName` union type for compile-time safety
|
|
45
|
+
- Deep config validation (guards against malformed JSON, null values, non-array profiles, non-string elements)
|
|
46
|
+
- Drift-detection integration test verifies `ALL_TOOL_NAMES` stays in sync with `index.ts` tool definitions
|
|
47
|
+
- Defense-in-depth: both `ListTools` filtering and `CallTool` guard for disabled tools
|
|
48
|
+
- 100% backwards compatible — no env var + no config = all 38 tools (existing behavior unchanged)
|
|
49
|
+
- All 1185 tests passing across Node.js 20, 22, and 24
|
|
50
|
+
|
|
10
51
|
## [0.11.0] - 2025-12-10
|
|
11
52
|
|
|
12
53
|
### Breaking Changes
|
|
@@ -466,7 +507,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
466
507
|
- **Security**: Security updates
|
|
467
508
|
- **Technical**: Internal improvements
|
|
468
509
|
|
|
469
|
-
[Unreleased]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.
|
|
510
|
+
[Unreleased]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.12.1...HEAD
|
|
511
|
+
[0.12.1]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.12.0...v0.12.1
|
|
512
|
+
[0.12.0]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.11.0...v0.12.0
|
|
513
|
+
[0.11.0]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.10.2...v0.11.0
|
|
514
|
+
[0.10.2]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.10.1...v0.10.2
|
|
515
|
+
[0.10.1]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.10.0...v0.10.1
|
|
470
516
|
[0.10.0]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.9.0...v0.10.0
|
|
471
517
|
[0.9.0]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.8.4...v0.9.0
|
|
472
518
|
[0.8.4]: https://github.com/mkreyman/mcp-memory-keeper/compare/v0.8.3...v0.8.4
|
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ npm install
|
|
|
174
174
|
npm run build
|
|
175
175
|
|
|
176
176
|
# 4. Add to Claude
|
|
177
|
-
claude mcp add memory-keeper
|
|
177
|
+
claude mcp add memory-keeper /absolute/path/to/mcp-memory-keeper/bin/mcp-memory-keeper
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
</details>
|
|
@@ -213,6 +213,92 @@ export MCP_MAX_ITEMS=50 # Fewer items per response
|
|
|
213
213
|
export MCP_CHARS_PER_TOKEN=3.0 # More conservative estimation (optional)
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
#### Tool Profiles
|
|
217
|
+
|
|
218
|
+
By default, all 38 tools are exposed. To reduce context overhead in your AI assistant, you can activate a tool profile that limits which tools are available.
|
|
219
|
+
|
|
220
|
+
**Quick usage:**
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Essential tools only (8 tools)
|
|
224
|
+
TOOL_PROFILE=minimal npx mcp-memory-keeper
|
|
225
|
+
|
|
226
|
+
# Standard workflow set (22 tools)
|
|
227
|
+
TOOL_PROFILE=standard npx mcp-memory-keeper
|
|
228
|
+
|
|
229
|
+
# All tools (default)
|
|
230
|
+
TOOL_PROFILE=full npx mcp-memory-keeper
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Built-in profiles:**
|
|
234
|
+
|
|
235
|
+
| Profile | Tools | Description |
|
|
236
|
+
| ---------- | ----- | -------------------------------------------------------------- |
|
|
237
|
+
| `minimal` | 8 | Core persistence: save, get, search, status, checkpoint |
|
|
238
|
+
| `standard` | 22 | Daily workflow: core + git, batch ops, channels, export/import |
|
|
239
|
+
| `full` | 38 | All tools (default, backwards compatible) |
|
|
240
|
+
|
|
241
|
+
**Custom profiles via config file:**
|
|
242
|
+
|
|
243
|
+
Create `~/.mcp-memory-keeper/config.json` to define or override profiles:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"profiles": {
|
|
248
|
+
"my_workflow": [
|
|
249
|
+
"context_session_start",
|
|
250
|
+
"context_save",
|
|
251
|
+
"context_get",
|
|
252
|
+
"context_search",
|
|
253
|
+
"context_checkpoint",
|
|
254
|
+
"context_restore_checkpoint",
|
|
255
|
+
"context_diff",
|
|
256
|
+
"context_timeline"
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Then activate it: `TOOL_PROFILE=my_workflow npx mcp-memory-keeper`
|
|
263
|
+
|
|
264
|
+
Config file profiles take precedence over built-in defaults with the same name.
|
|
265
|
+
|
|
266
|
+
**Profile resolution precedence:**
|
|
267
|
+
|
|
268
|
+
| `TOOL_PROFILE` | Config file has profile? | Built-in exists? | Result |
|
|
269
|
+
| -------------- | ------------------------ | ---------------- | -------------------------------- |
|
|
270
|
+
| Set | Yes | — | Uses config file definition |
|
|
271
|
+
| Set | No | Yes | Uses built-in definition |
|
|
272
|
+
| Set | No | No | Warning + falls back to `full` |
|
|
273
|
+
| Not set | — | — | Uses built-in `full` (all tools) |
|
|
274
|
+
|
|
275
|
+
**Environment variables:**
|
|
276
|
+
|
|
277
|
+
| Variable | Description |
|
|
278
|
+
| --------------------- | ------------------------------------------------------------------------- |
|
|
279
|
+
| `TOOL_PROFILE` | Profile name to activate (e.g., `minimal`, `standard`, `full`, or custom) |
|
|
280
|
+
| `TOOL_PROFILE_CONFIG` | Override config file path (default: `~/.mcp-memory-keeper/config.json`) |
|
|
281
|
+
|
|
282
|
+
> Note: Profile resolution happens once at server startup. Changes to the env var or config file take effect on the next server restart.
|
|
283
|
+
|
|
284
|
+
**Claude Code / Claude Desktop configuration:**
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"mcpServers": {
|
|
289
|
+
"memory-keeper": {
|
|
290
|
+
"command": "npx",
|
|
291
|
+
"args": ["mcp-memory-keeper"],
|
|
292
|
+
"env": {
|
|
293
|
+
"TOOL_PROFILE": "minimal"
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
See `examples/config.json` for a complete example config file.
|
|
301
|
+
|
|
216
302
|
### Claude Code (CLI)
|
|
217
303
|
|
|
218
304
|
#### Configuration Scopes
|
|
@@ -1126,6 +1212,38 @@ Test categories:
|
|
|
1126
1212
|
- [ ] Custom context templates
|
|
1127
1213
|
- [ ] Automatic retention policies
|
|
1128
1214
|
|
|
1215
|
+
## Upgrading
|
|
1216
|
+
|
|
1217
|
+
### Database path change (v0.12.x+)
|
|
1218
|
+
|
|
1219
|
+
Prior to this release, the server resolved `context.db` relative to the process's current working directory. The database now lives at an absolute path:
|
|
1220
|
+
|
|
1221
|
+
- **Default:** `~/mcp-data/memory-keeper/context.db`
|
|
1222
|
+
- **Custom:** set `DATA_DIR=/your/path` — the server will use `$DATA_DIR/context.db`
|
|
1223
|
+
|
|
1224
|
+
If you have existing data in a `context.db` in your old working directory, move it to the new location before restarting the server:
|
|
1225
|
+
|
|
1226
|
+
```bash
|
|
1227
|
+
mkdir -p ~/mcp-data/memory-keeper
|
|
1228
|
+
cp /path/to/old/context.db ~/mcp-data/memory-keeper/context.db
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
If `DATA_DIR` is set, use that path as the destination instead of `~/mcp-data/memory-keeper/`.
|
|
1232
|
+
|
|
1233
|
+
The server will print a warning to stderr if it detects a `context.db` in the current directory that differs from the configured data directory, including the exact `cp` command to run.
|
|
1234
|
+
|
|
1235
|
+
### From-source install command change
|
|
1236
|
+
|
|
1237
|
+
If you registered memory-keeper using `node dist/index.js` directly, update your MCP config to use the bin wrapper instead:
|
|
1238
|
+
|
|
1239
|
+
```bash
|
|
1240
|
+
# remove the old entry
|
|
1241
|
+
claude mcp remove memory-keeper
|
|
1242
|
+
|
|
1243
|
+
# add the updated entry
|
|
1244
|
+
claude mcp add memory-keeper /absolute/path/to/mcp-memory-keeper/bin/mcp-memory-keeper
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1129
1247
|
## Contributing
|
|
1130
1248
|
|
|
1131
1249
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -55,9 +55,12 @@ describe('Git Integration Tests', () => {
|
|
|
55
55
|
// Create and initialize a real git repo for testing
|
|
56
56
|
fs.mkdirSync(tempRepoPath, { recursive: true });
|
|
57
57
|
git = (0, simple_git_1.simpleGit)(tempRepoPath);
|
|
58
|
-
await git.init();
|
|
58
|
+
await git.init(['--initial-branch=master']);
|
|
59
59
|
await git.addConfig('user.name', 'Test User');
|
|
60
60
|
await git.addConfig('user.email', 'test@example.com');
|
|
61
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
62
|
+
const localHooksDir = path.join(tempRepoPath, '.git', 'hooks');
|
|
63
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
61
64
|
// Create initial commit
|
|
62
65
|
fs.writeFileSync(path.join(tempRepoPath, 'README.md'), '# Test Repo');
|
|
63
66
|
await git.add('.');
|
|
@@ -55,6 +55,9 @@ describe('Project Directory Feature Tests', () => {
|
|
|
55
55
|
await git.init();
|
|
56
56
|
await git.addConfig('user.name', 'Test User');
|
|
57
57
|
await git.addConfig('user.email', 'test@example.com');
|
|
58
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
59
|
+
const localHooksDir = path.join(tempProjectPath, '.git', 'hooks');
|
|
60
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
58
61
|
// Create initial commit
|
|
59
62
|
fs.writeFileSync(path.join(tempProjectPath, 'README.md'), '# Test Project');
|
|
60
63
|
await git.add('.');
|
|
@@ -242,6 +245,9 @@ describe('Project Directory Feature Tests', () => {
|
|
|
242
245
|
// Configure git for this test to avoid CI failures
|
|
243
246
|
await git.addConfig('user.name', 'Test User');
|
|
244
247
|
await git.addConfig('user.email', 'test@example.com');
|
|
248
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
249
|
+
const localHooksDir = path.join(pathWithSpaces, '.git', 'hooks');
|
|
250
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
245
251
|
try {
|
|
246
252
|
fs.writeFileSync(path.join(pathWithSpaces, 'test.txt'), 'content');
|
|
247
253
|
await git.add('.');
|
|
@@ -86,12 +86,11 @@ const os = __importStar(require("os"));
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
(0, globals_1.it)('should start server and respond to initialize request', done => {
|
|
89
|
-
const dbPath = path.join(tempDir, 'test.db');
|
|
90
89
|
// Start the server
|
|
91
90
|
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
92
91
|
env: {
|
|
93
92
|
...process.env,
|
|
94
|
-
|
|
93
|
+
DATA_DIR: tempDir,
|
|
95
94
|
},
|
|
96
95
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
97
96
|
});
|
|
@@ -161,6 +160,7 @@ const os = __importStar(require("os"));
|
|
|
161
160
|
// Start the server
|
|
162
161
|
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
163
162
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
163
|
+
env: { ...process.env, DATA_DIR: tempDir },
|
|
164
164
|
});
|
|
165
165
|
// Track for global cleanup
|
|
166
166
|
if (!global.testProcesses) {
|
|
@@ -190,11 +190,10 @@ const os = __importStar(require("os"));
|
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
(0, globals_1.it)('should handle invalid requests gracefully', done => {
|
|
193
|
-
const dbPath = path.join(tempDir, 'test.db');
|
|
194
193
|
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
195
194
|
env: {
|
|
196
195
|
...process.env,
|
|
197
|
-
|
|
196
|
+
DATA_DIR: tempDir,
|
|
198
197
|
},
|
|
199
198
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
200
199
|
});
|
|
@@ -256,11 +255,10 @@ const os = __importStar(require("os"));
|
|
|
256
255
|
}, 5000);
|
|
257
256
|
});
|
|
258
257
|
(0, globals_1.it)('should handle server shutdown gracefully', done => {
|
|
259
|
-
const dbPath = path.join(tempDir, 'test.db');
|
|
260
258
|
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
261
259
|
env: {
|
|
262
260
|
...process.env,
|
|
263
|
-
|
|
261
|
+
DATA_DIR: tempDir,
|
|
264
262
|
},
|
|
265
263
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
266
264
|
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const globals_1 = require("@jest/globals");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const tool_profiles_1 = require("../../utils/tool-profiles");
|
|
40
|
+
/**
|
|
41
|
+
* Drift-detection: extract tool names from the ListToolsRequestSchema handler
|
|
42
|
+
* in src/index.ts to verify ALL_TOOL_NAMES stays in sync with actual tool definitions.
|
|
43
|
+
*/
|
|
44
|
+
function extractToolNamesFromIndexTs() {
|
|
45
|
+
const indexPath = path.join(__dirname, '..', '..', 'index.ts');
|
|
46
|
+
const src = fs.readFileSync(indexPath, 'utf-8');
|
|
47
|
+
// Find the allTools array: starts after "const allTools" and ends at the matching "];"
|
|
48
|
+
// We look for tool name strings inside the ListToolsRequestSchema handler
|
|
49
|
+
const toolNameRegex = /^\s+name:\s+'(context_[a-z_]+)'/gm;
|
|
50
|
+
const names = [];
|
|
51
|
+
let match;
|
|
52
|
+
// Only capture tool names outside of block comments (skip commented-out tools)
|
|
53
|
+
// Split by block comment boundaries and only scan non-comment sections
|
|
54
|
+
const sections = src.split(/\/\*[\s\S]*?\*\//);
|
|
55
|
+
for (const section of sections) {
|
|
56
|
+
toolNameRegex.lastIndex = 0;
|
|
57
|
+
while ((match = toolNameRegex.exec(section)) !== null) {
|
|
58
|
+
names.push(match[1]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return names;
|
|
62
|
+
}
|
|
63
|
+
(0, globals_1.describe)('Tool Profile Integration Tests', () => {
|
|
64
|
+
const originalEnv = process.env.TOOL_PROFILE;
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
if (originalEnv !== undefined) {
|
|
67
|
+
process.env.TOOL_PROFILE = originalEnv;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
delete process.env.TOOL_PROFILE;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
(0, globals_1.describe)('Drift detection: ALL_TOOL_NAMES vs index.ts', () => {
|
|
74
|
+
(0, globals_1.it)('ALL_TOOL_NAMES should match active tools defined in index.ts', () => {
|
|
75
|
+
const indexToolNames = extractToolNamesFromIndexTs();
|
|
76
|
+
const allToolNamesArray = [...tool_profiles_1.ALL_TOOL_NAMES];
|
|
77
|
+
// Same count
|
|
78
|
+
(0, globals_1.expect)(allToolNamesArray.length).toBe(indexToolNames.length);
|
|
79
|
+
// Same set of names
|
|
80
|
+
(0, globals_1.expect)(new Set(allToolNamesArray)).toEqual(new Set(indexToolNames));
|
|
81
|
+
});
|
|
82
|
+
(0, globals_1.it)('should not include commented-out tools', () => {
|
|
83
|
+
// context_share and context_get_shared are commented out in index.ts
|
|
84
|
+
(0, globals_1.expect)(tool_profiles_1.ALL_TOOL_NAMES_SET.has('context_share')).toBe(false);
|
|
85
|
+
(0, globals_1.expect)(tool_profiles_1.ALL_TOOL_NAMES_SET.has('context_get_shared')).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
(0, globals_1.describe)('Profile filtering behavior', () => {
|
|
89
|
+
(0, globals_1.it)('minimal profile should include core tools and exclude advanced tools', () => {
|
|
90
|
+
process.env.TOOL_PROFILE = 'minimal';
|
|
91
|
+
const profile = (0, tool_profiles_1.resolveActiveProfile)('/nonexistent/config.json');
|
|
92
|
+
// Core tools present
|
|
93
|
+
(0, globals_1.expect)(profile.tools.has('context_save')).toBe(true);
|
|
94
|
+
(0, globals_1.expect)(profile.tools.has('context_get')).toBe(true);
|
|
95
|
+
(0, globals_1.expect)(profile.tools.has('context_search')).toBe(true);
|
|
96
|
+
(0, globals_1.expect)(profile.tools.has('context_checkpoint')).toBe(true);
|
|
97
|
+
// Advanced tools absent
|
|
98
|
+
(0, globals_1.expect)(profile.tools.has('context_analyze')).toBe(false);
|
|
99
|
+
(0, globals_1.expect)(profile.tools.has('context_visualize')).toBe(false);
|
|
100
|
+
(0, globals_1.expect)(profile.tools.has('context_delegate')).toBe(false);
|
|
101
|
+
(0, globals_1.expect)(profile.tools.has('context_semantic_search')).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
(0, globals_1.it)('default (no env var) should expose all tools with backwards-compatible behavior', () => {
|
|
104
|
+
delete process.env.TOOL_PROFILE;
|
|
105
|
+
const profile = (0, tool_profiles_1.resolveActiveProfile)('/nonexistent/config.json');
|
|
106
|
+
(0, globals_1.expect)(profile.tools.size).toBe(tool_profiles_1.ALL_TOOL_NAMES.length);
|
|
107
|
+
(0, globals_1.expect)(profile.profileName).toBe('full');
|
|
108
|
+
(0, globals_1.expect)(profile.source).toBe('default');
|
|
109
|
+
(0, globals_1.expect)(profile.warnings).toHaveLength(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
(0, globals_1.describe)('CallTool guard behavior', () => {
|
|
113
|
+
(0, globals_1.it)('disabled tool should be in ALL_TOOL_NAMES_SET but not in profile tools', () => {
|
|
114
|
+
process.env.TOOL_PROFILE = 'minimal';
|
|
115
|
+
const profile = (0, tool_profiles_1.resolveActiveProfile)('/nonexistent/config.json');
|
|
116
|
+
const disabledTool = 'context_analyze';
|
|
117
|
+
// The guard logic: known tool that is not enabled
|
|
118
|
+
const isKnown = tool_profiles_1.ALL_TOOL_NAMES_SET.has(disabledTool);
|
|
119
|
+
const isEnabled = profile.tools.has(disabledTool);
|
|
120
|
+
(0, globals_1.expect)(isKnown).toBe(true);
|
|
121
|
+
(0, globals_1.expect)(isEnabled).toBe(false);
|
|
122
|
+
// In index.ts: isKnown && !isEnabled → return isError: true
|
|
123
|
+
});
|
|
124
|
+
(0, globals_1.it)('unknown tool should not be in ALL_TOOL_NAMES_SET (falls through to default switch)', () => {
|
|
125
|
+
const unknownTool = 'non_existent_tool';
|
|
126
|
+
(0, globals_1.expect)(tool_profiles_1.ALL_TOOL_NAMES_SET.has(unknownTool)).toBe(false);
|
|
127
|
+
// In index.ts: !isKnown → falls through to default: throw new Error()
|
|
128
|
+
});
|
|
129
|
+
(0, globals_1.it)('enabled tool should pass both checks', () => {
|
|
130
|
+
process.env.TOOL_PROFILE = 'minimal';
|
|
131
|
+
const profile = (0, tool_profiles_1.resolveActiveProfile)('/nonexistent/config.json');
|
|
132
|
+
const enabledTool = 'context_save';
|
|
133
|
+
const isKnown = tool_profiles_1.ALL_TOOL_NAMES_SET.has(enabledTool);
|
|
134
|
+
const isEnabled = profile.tools.has(enabledTool);
|
|
135
|
+
(0, globals_1.expect)(isKnown).toBe(true);
|
|
136
|
+
(0, globals_1.expect)(isEnabled).toBe(true);
|
|
137
|
+
// In index.ts: isKnown && isEnabled → guard does not fire, proceeds to switch
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
(0, globals_1.describe)('TOOL_PROFILE_CONFIG support', () => {
|
|
141
|
+
(0, globals_1.it)('resolveActiveProfile accepts custom config path (used by TOOL_PROFILE_CONFIG)', () => {
|
|
142
|
+
// This tests the mechanism that index.ts uses:
|
|
143
|
+
// resolveActiveProfile(process.env.TOOL_PROFILE_CONFIG)
|
|
144
|
+
const result = (0, tool_profiles_1.resolveActiveProfile)('/nonexistent/custom/path/config.json');
|
|
145
|
+
// Missing file → no config → falls back to built-in 'full'
|
|
146
|
+
(0, globals_1.expect)(result.profileName).toBe('full');
|
|
147
|
+
(0, globals_1.expect)(result.tools.size).toBe(38);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -123,6 +123,9 @@ Tip: Initialize git with 'git init' to enable git tracking features.`;
|
|
|
123
123
|
await git.init();
|
|
124
124
|
await git.addConfig('user.name', 'Test User');
|
|
125
125
|
await git.addConfig('user.email', 'test@example.com');
|
|
126
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
127
|
+
const localHooksDir = path.join(tempRepoPath, '.git', 'hooks');
|
|
128
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
126
129
|
});
|
|
127
130
|
afterEach(() => {
|
|
128
131
|
fs.rmSync(tempRepoPath, { recursive: true, force: true });
|