bantamkit-mcp 0.34.2 → 0.34.3
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/README.md +7 -4
- package/dist/eventlog.js +8 -9
- package/dist/hostinstall.js +22 -0
- package/dist/memory/store.d.ts +29 -0
- package/dist/memory/store.js +45 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ intentional difference written down as a ruling.
|
|
|
30
30
|
| [The operator CLI: `bantamkit-memory`](#the-operator-cli-bantamkit-memory) | The second bin: status, lint, compact, archive, restore |
|
|
31
31
|
| [Memory stores are layered by default](#memory-stores-are-layered-by-default) | Why not to add `--store` by reflex |
|
|
32
32
|
| [The asset pack](#the-asset-pack) | What ships in `assets/` and how it is found |
|
|
33
|
-
| [
|
|
33
|
+
| [Where the Python and Node servers differ](#where-the-python-and-node-servers-differ) | One store; concurrent writes, one hand-edit to avoid |
|
|
34
34
|
| [Development](#development) | Build, test, conformance |
|
|
35
35
|
| [Measurements and history](#measurements-and-history) | Where the measured numbers behind this page live |
|
|
36
36
|
|
|
@@ -261,7 +261,7 @@ dependency in a temp directory that will be deleted, and `npm update` cannot hel
|
|
|
261
261
|
| Two machines with one config run different builds | [Silent version float](#silent-version-float): compare `build_id` |
|
|
262
262
|
| `bantamkit_status` still shows the old version after an update | The host has not reconnected; restart the server |
|
|
263
263
|
| Recall finds nothing, or the wrong store | Set `BANTAMKIT_MEMORY_DIR`; the reply names the store it searched |
|
|
264
|
-
| `malformed fact file …` | A hand-edited fact has a bare non-string field; see [
|
|
264
|
+
| `malformed fact file …` | A hand-edited fact has a bare non-string field; see [Where the Python and Node servers differ](#where-the-python-and-node-servers-differ) |
|
|
265
265
|
| `AssetNotFound: no assets directory found; set BANTAMKIT_ASSETS` | See [The asset pack](#the-asset-pack) |
|
|
266
266
|
|
|
267
267
|
## Configuration
|
|
@@ -381,9 +381,12 @@ the pack ships whole — 91 files / 238,751 bytes on 2026-09-15 — or `assets_d
|
|
|
381
381
|
3. `<repo>/assets/` — two levels above `dist/`, for a dev checkout with nothing vendored.
|
|
382
382
|
4. Otherwise `AssetNotFound: no assets directory found; set BANTAMKIT_ASSETS`.
|
|
383
383
|
|
|
384
|
-
##
|
|
384
|
+
## Where the Python and Node servers differ
|
|
385
385
|
|
|
386
|
-
Both servers read and write the same
|
|
386
|
+
Both servers read and write the same on-disk store; byte-compatibility is the product. Every
|
|
387
|
+
feature lands in both in one change, and a conformance case compares their answers. Differences
|
|
388
|
+
are deliberate and each is ruled in the
|
|
389
|
+
[divergence table](https://github.com/Ink01101011/bantamkit/blob/main/docs/porting.md#where-the-two-runtimes-deliberately-differ):
|
|
387
390
|
|
|
388
391
|
- **Concurrent writes are safe for the memory store and lossy for the checkpoint, in both
|
|
389
392
|
runtimes.** `index.md` is derived from `facts/`, so the last writer re-enumerates everything
|
package/dist/eventlog.js
CHANGED
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
* notice — the failure mode a diagnostic can least afford.
|
|
56
56
|
*/
|
|
57
57
|
import { appendFileSync, mkdirSync, renameSync, statSync } from 'node:fs';
|
|
58
|
-
import { cmpCodepoint, osErrorClassName, pyExists, PyOSError, pyJoin, pyName, pyParent
|
|
59
|
-
import { ensureBantamkitGitignore } from './memory/store.js';
|
|
58
|
+
import { cmpCodepoint, osErrorClassName, pyExists, PyOSError, pyJoin, pyName, pyParent } from './memory/pyfs.js';
|
|
59
|
+
import { bantamkitDirFor, ensureBantamkitGitignore } from './memory/store.js';
|
|
60
60
|
/**
|
|
61
61
|
* Environment switch. Unset or `off`/`0`/`false`/`no`/empty -> disabled. `on`/`1`/`true`/
|
|
62
62
|
* `yes` -> the default file inside the memory store. Anything else is taken as the literal
|
|
@@ -254,13 +254,12 @@ export class EventLog {
|
|
|
254
254
|
// USER RULING #2 (J51-8b): the ignore file is written only when THIS call is what
|
|
255
255
|
// creates `.bantamkit`, so existence has to be checked BEFORE the mkdir below -- after
|
|
256
256
|
// it, the directory unconditionally exists and the question is unanswerable.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
257
|
+
//
|
|
258
|
+
// J54-3: the walk moved into `bantamkitDirFor` beside the writer it feeds, so the two
|
|
259
|
+
// creators in this runtime ask the same question in the same words rather than keeping a
|
|
260
|
+
// copy each. The answer here is unchanged -- a log file is never itself named
|
|
261
|
+
// `.bantamkit`, so self-or-ancestors and ancestors give the same directory.
|
|
262
|
+
const bantamkitDir = bantamkitDirFor(path);
|
|
264
263
|
const bantamkitDirExistedBefore = bantamkitDir !== null && pyExists(bantamkitDir);
|
|
265
264
|
// `pyParent` and not `node:path`'s `dirname`: this module is reached from a Windows
|
|
266
265
|
// host through the same code, and the reference's `Path(...).parent` is the spelling
|
package/dist/hostinstall.js
CHANGED
|
@@ -37,6 +37,7 @@ import { homedir } from 'node:os';
|
|
|
37
37
|
import { dirname, join } from 'node:path';
|
|
38
38
|
import { fileURLToPath } from 'node:url';
|
|
39
39
|
import { currentInstall, Undetermined } from './mcp/identity.js';
|
|
40
|
+
import { bantamkitDirFor, ensureBantamkitGitignore } from './memory/store.js';
|
|
40
41
|
import { compareVersions, keptCli, keptManifest, keptPrefix, PACKAGE, runInstaller, shlexJoin } from './npminstall.js';
|
|
41
42
|
import { dumpJson, fromJs } from './pyjson.js';
|
|
42
43
|
export { keptCli, keptPrefix };
|
|
@@ -319,7 +320,28 @@ export function thisCommand(options = {}) {
|
|
|
319
320
|
}
|
|
320
321
|
const command = ['npm', 'install', '--prefix', prefix, `${PACKAGE}@${version}`];
|
|
321
322
|
const rendered = shlexJoin(command);
|
|
323
|
+
// THE THIRD `.bantamkit` CREATOR (J54-3), and the one that creates the most of it: npm makes
|
|
324
|
+
// a missing `--prefix` itself, so this line — not `MemoryStore`, not `EventLog` — is what
|
|
325
|
+
// brings `<homedir>/.bantamkit` into existence on a machine that has never saved a memory.
|
|
326
|
+
// Measured on the machine this was found on: 28 MB of install tree under a `~/.bantamkit`
|
|
327
|
+
// with no `.gitignore`, every byte of it untracked in a `$HOME` that is a git repository.
|
|
328
|
+
//
|
|
329
|
+
// Checked BEFORE npm runs, per user ruling #2, for the reason both other creators check it
|
|
330
|
+
// before their own mkdir: afterwards the directory exists either way and the question
|
|
331
|
+
// "did THIS call create it" is unanswerable. A `~/.bantamkit` that was already there —
|
|
332
|
+
// holding an older kept install, a memory store, or an ignore file the operator deleted on
|
|
333
|
+
// purpose — is left exactly as it is.
|
|
334
|
+
const bantamkitDir = bantamkitDirFor(prefix);
|
|
335
|
+
const bantamkitDirExistedBefore = bantamkitDir !== null && existsSync(bantamkitDir);
|
|
322
336
|
const [code, output] = (options.installer ?? runInstaller)(command);
|
|
337
|
+
// BEFORE THE REFUSALS, AND THAT ORDER IS MEASURED, not tidy. A `npm install --prefix` that
|
|
338
|
+
// FAILS still leaves the prefix behind: measured 2026-09-18 with `npm_config_offline=true`
|
|
339
|
+
// against an empty cache — exit 1, and `<home>/.bantamkit/mcp` there afterwards. A refusal
|
|
340
|
+
// that returned first would leave exactly the directory this closes over: made by bantamkit's
|
|
341
|
+
// own command, holding nothing anybody asked for, and visible to git. The helper never
|
|
342
|
+
// creates `bantamkitDir` itself, so an installer that made nothing writes nothing here.
|
|
343
|
+
if (bantamkitDir !== null)
|
|
344
|
+
ensureBantamkitGitignore(bantamkitDir, !bantamkitDirExistedBefore);
|
|
323
345
|
const refused = `could not make the kept install at ${prefix}, so no host configuration was changed: ${rendered} exited ${code}`;
|
|
324
346
|
if (code !== 0)
|
|
325
347
|
throw new InstallError(`${refused}: ${lastLine(output)}`);
|
package/dist/memory/store.d.ts
CHANGED
|
@@ -33,6 +33,14 @@ export declare const BANTAMKIT_GITIGNORE_TEXT: string;
|
|
|
33
33
|
* a `.bantamkit` that git can see (or ignore one it should not), which is the whole bug this
|
|
34
34
|
* closes.
|
|
35
35
|
*
|
|
36
|
+
* THE CREATORS, ENUMERATED (J54-3), because "anything else" is how one got missed: in THIS
|
|
37
|
+
* runtime there are exactly three — `MemoryStore.ensureDirs`, `EventLog`'s append-time
|
|
38
|
+
* `mkdirSync`, and `hostinstall.thisCommand`, where `--install` from an `npx` cache has npm
|
|
39
|
+
* create `<homedir>/.bantamkit/mcp` (npm makes a missing `--prefix` itself). The first two ask
|
|
40
|
+
* `bantamkitDirFor` which directory the decision is about rather than assuming the root's
|
|
41
|
+
* parent is it; the third is the one the reference does not have at all, because the offline
|
|
42
|
+
* install is Node-only by user ruling. `docs/porting.md` carries the row.
|
|
43
|
+
*
|
|
36
44
|
* NEVER REWRITES. A `.gitignore` that already exists -- whatever its bytes -- is left
|
|
37
45
|
* exactly as it is, kept as a second guard even when `created` is `true`: an operator who
|
|
38
46
|
* deleted it to commit the directory keeps that decision, and this never diffs its own
|
|
@@ -48,6 +56,27 @@ export declare const BANTAMKIT_GITIGNORE_TEXT: string;
|
|
|
48
56
|
* bring `.bantamkit` into existence early.
|
|
49
57
|
*/
|
|
50
58
|
export declare function ensureBantamkitGitignore(directory: string, created: boolean): void;
|
|
59
|
+
/**
|
|
60
|
+
* The `.bantamkit` directory a recursive mkdir of `path` would create or fill, or `null` when
|
|
61
|
+
* there is none: `path` itself if it is named `.bantamkit`, otherwise its nearest ancestor
|
|
62
|
+
* that is. `runtime-py/src/bantamkit/memory/store.py`'s `bantamkit_dir_for` is the reference.
|
|
63
|
+
*
|
|
64
|
+
* WHY A WALK AND NOT `pyParent(path)` (J54-3). Every creator has to hand
|
|
65
|
+
* `ensureBantamkitGitignore` the directory the decision is ABOUT, and the parent is the right
|
|
66
|
+
* answer for exactly one shape of path, `<x>/.bantamkit/memory`. A store rooted AT the
|
|
67
|
+
* `.bantamkit` directory (`--store ~/.bantamkit`) or nested deeper under it creates a
|
|
68
|
+
* `.bantamkit` whose gitignore decision was taken about the wrong directory — so no decision
|
|
69
|
+
* was taken at all, and git could see the whole tree. MEASURED, not hypothetical: `~/.bantamkit`
|
|
70
|
+
* on the machine this was found on holds an empty `facts/` and `archive/` beside `memory/`,
|
|
71
|
+
* left by a store once rooted at it, and no `.gitignore`.
|
|
72
|
+
*
|
|
73
|
+
* NEAREST AND NOT OUTERMOST, which is the choice `EventLog`'s append already made and this
|
|
74
|
+
* function now carries for both creators.
|
|
75
|
+
*
|
|
76
|
+
* READ-ONLY. It looks at names, never at the filesystem, so it is equally valid before or after
|
|
77
|
+
* the mkdir — but `created` is answered by checking THIS path's existence BEFORE.
|
|
78
|
+
*/
|
|
79
|
+
export declare function bantamkitDirFor(path: string): string | null;
|
|
51
80
|
/** `sorted(VALID_TYPES)` is what the error message interpolates, so the order is load-bearing. */
|
|
52
81
|
export declare const VALID_TYPES: readonly ["feedback", "project", "reference", "user"];
|
|
53
82
|
/**
|
package/dist/memory/store.js
CHANGED
|
@@ -42,7 +42,7 @@ import { basename } from 'node:path';
|
|
|
42
42
|
import { BantamError } from '../errors.js';
|
|
43
43
|
import { formatFact, parseFrontmatter, pySplit, pyStrip, todayLocal } from './factfile.js';
|
|
44
44
|
import { PyScalar } from './pyyaml.js';
|
|
45
|
-
import { asPyOSError, cmpCodepoint, matchesMd, pyExists, pyJoin, pyLexists, pyMkdirParents, pyMtimeDate, pyName, pyParent, pyReadText, pyReplace, pyScandirNames, pySuffix, pyUnlink, pyWithSuffix, pyWriteText, PyOSError, sortedPathNames, } from './pyfs.js';
|
|
45
|
+
import { asPyOSError, cmpCodepoint, matchesMd, pyExists, pyJoin, pyLexists, pyMkdirParents, pyMtimeDate, pyName, pyParent, pyParents, pyReadText, pyReplace, pyScandirNames, pySuffix, pyUnlink, pyWithSuffix, pyWriteText, PyOSError, sortedPathNames, } from './pyfs.js';
|
|
46
46
|
/**
|
|
47
47
|
* The exact bytes written into a `.bantamkit/.gitignore` the first time such a directory is
|
|
48
48
|
* brought into existence (user ruling 2026-09-15, `.shiftwork/notes-job51/P0-probes.md`).
|
|
@@ -76,6 +76,14 @@ export const BANTAMKIT_GITIGNORE_TEXT = '# Created by bantamkit: this directory
|
|
|
76
76
|
* a `.bantamkit` that git can see (or ignore one it should not), which is the whole bug this
|
|
77
77
|
* closes.
|
|
78
78
|
*
|
|
79
|
+
* THE CREATORS, ENUMERATED (J54-3), because "anything else" is how one got missed: in THIS
|
|
80
|
+
* runtime there are exactly three — `MemoryStore.ensureDirs`, `EventLog`'s append-time
|
|
81
|
+
* `mkdirSync`, and `hostinstall.thisCommand`, where `--install` from an `npx` cache has npm
|
|
82
|
+
* create `<homedir>/.bantamkit/mcp` (npm makes a missing `--prefix` itself). The first two ask
|
|
83
|
+
* `bantamkitDirFor` which directory the decision is about rather than assuming the root's
|
|
84
|
+
* parent is it; the third is the one the reference does not have at all, because the offline
|
|
85
|
+
* install is Node-only by user ruling. `docs/porting.md` carries the row.
|
|
86
|
+
*
|
|
79
87
|
* NEVER REWRITES. A `.gitignore` that already exists -- whatever its bytes -- is left
|
|
80
88
|
* exactly as it is, kept as a second guard even when `created` is `true`: an operator who
|
|
81
89
|
* deleted it to commit the directory keeps that decision, and this never diffs its own
|
|
@@ -103,6 +111,33 @@ export function ensureBantamkitGitignore(directory, created) {
|
|
|
103
111
|
/* best-effort: a missing ignore file is not worth a lost fact */
|
|
104
112
|
}
|
|
105
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* The `.bantamkit` directory a recursive mkdir of `path` would create or fill, or `null` when
|
|
116
|
+
* there is none: `path` itself if it is named `.bantamkit`, otherwise its nearest ancestor
|
|
117
|
+
* that is. `runtime-py/src/bantamkit/memory/store.py`'s `bantamkit_dir_for` is the reference.
|
|
118
|
+
*
|
|
119
|
+
* WHY A WALK AND NOT `pyParent(path)` (J54-3). Every creator has to hand
|
|
120
|
+
* `ensureBantamkitGitignore` the directory the decision is ABOUT, and the parent is the right
|
|
121
|
+
* answer for exactly one shape of path, `<x>/.bantamkit/memory`. A store rooted AT the
|
|
122
|
+
* `.bantamkit` directory (`--store ~/.bantamkit`) or nested deeper under it creates a
|
|
123
|
+
* `.bantamkit` whose gitignore decision was taken about the wrong directory — so no decision
|
|
124
|
+
* was taken at all, and git could see the whole tree. MEASURED, not hypothetical: `~/.bantamkit`
|
|
125
|
+
* on the machine this was found on holds an empty `facts/` and `archive/` beside `memory/`,
|
|
126
|
+
* left by a store once rooted at it, and no `.gitignore`.
|
|
127
|
+
*
|
|
128
|
+
* NEAREST AND NOT OUTERMOST, which is the choice `EventLog`'s append already made and this
|
|
129
|
+
* function now carries for both creators.
|
|
130
|
+
*
|
|
131
|
+
* READ-ONLY. It looks at names, never at the filesystem, so it is equally valid before or after
|
|
132
|
+
* the mkdir — but `created` is answered by checking THIS path's existence BEFORE.
|
|
133
|
+
*/
|
|
134
|
+
export function bantamkitDirFor(path) {
|
|
135
|
+
for (const candidate of [path, ...pyParents(path)]) {
|
|
136
|
+
if (pyName(candidate) === '.bantamkit')
|
|
137
|
+
return candidate;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
106
141
|
/** `sorted(VALID_TYPES)` is what the error message interpolates, so the order is load-bearing. */
|
|
107
142
|
export const VALID_TYPES = ['feedback', 'project', 'reference', 'user'];
|
|
108
143
|
/**
|
|
@@ -598,8 +633,13 @@ export class MemoryStore {
|
|
|
598
633
|
// Checked BEFORE the mkdir below, per user ruling #2: this is the one moment that can
|
|
599
634
|
// tell whether `.bantamkit` already existed. After the mkdir it unconditionally exists
|
|
600
635
|
// either way, so the answer has to be captured now or not at all.
|
|
601
|
-
|
|
602
|
-
|
|
636
|
+
//
|
|
637
|
+
// J54-3: `bantamkitDirFor`, not `pyParent`. The mkdirs below create every missing
|
|
638
|
+
// component of `this.root` — a root that IS the `.bantamkit` directory, or one nested
|
|
639
|
+
// deeper under it, brought a `.bantamkit` into existence while the decision was being
|
|
640
|
+
// taken about some other directory, which meant no decision at all.
|
|
641
|
+
const bantamkitDir = bantamkitDirFor(this.root);
|
|
642
|
+
const bantamkitDirExistedBefore = bantamkitDir !== null && pyExists(bantamkitDir);
|
|
603
643
|
try {
|
|
604
644
|
pyMkdirParents(pyJoin(this.root, 'facts'));
|
|
605
645
|
pyMkdirParents(pyJoin(this.root, 'archive'));
|
|
@@ -610,7 +650,8 @@ export class MemoryStore {
|
|
|
610
650
|
throw new MemoryValidationError(`memory store could not be created: ${this.root}; the directory is not` +
|
|
611
651
|
' there and this filesystem would not make it, so nothing was written');
|
|
612
652
|
}
|
|
613
|
-
|
|
653
|
+
if (bantamkitDir !== null)
|
|
654
|
+
ensureBantamkitGitignore(bantamkitDir, !bantamkitDirExistedBefore);
|
|
614
655
|
}
|
|
615
656
|
// ---- ops ----
|
|
616
657
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bantamkit-mcp",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.3",
|
|
4
4
|
"description": "Memory MCP server for Claude Code, Cursor, VS Code Copilot and Claude Desktop: per-person memory, JSON Schema validation, shift-work tools. Pure Node; install once with npx, run offline.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|