@cairnkeep/cli 2.15.4 → 2.16.0
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 +20 -0
- package/CONTRIBUTING.md +12 -0
- package/README.md +84 -590
- package/docs/README.md +79 -0
- package/docs/agents.md +173 -0
- package/docs/ecosystem.md +23 -0
- package/docs/learning/CURRICULUM-MAP.md +1 -1
- package/docs/learning/FEATURE-GUIDE.md +2 -2
- package/docs/learning/README.md +3 -0
- package/docs/learning/lessons/L00-why-cairnkeep.md +3 -1
- package/docs/learning/lessons/L01-safe-trial.md +3 -3
- package/docs/learning/lessons/L02-installation.md +2 -2
- package/docs/learning/lessons/L03-first-project.md +9 -10
- package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
- package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
- package/docs/learning/lessons/L21-context-packs.md +1 -1
- package/docs/learning/lessons/L22-native-windows.md +2 -2
- package/docs/learning/lessons/L23-guided-setup.md +18 -5
- package/docs/learning/lessons/L24-okf-exchange.md +1 -1
- package/docs/learning/lessons/L25-playbooks.md +1 -1
- package/docs/native-windows.md +5 -0
- package/docs/operating.md +21 -3
- package/docs/quickstart.md +12 -4
- package/docs/releasing.md +27 -19
- package/examples/eval/bundled-fake.json +1 -1
- package/package.json +1 -1
- package/scripts/cairn-cli.mjs +1 -0
- package/scripts/setup.mjs +92 -13
- package/scripts/terminal-prompts.mjs +172 -0
- package/scripts/test-docs-navigation.sh +63 -0
- package/scripts/test-phase17-artifact-lifecycle.sh +1 -1
- package/scripts/test-release-sbom.sh +41 -16
- package/scripts/test-setup-preflight.mjs +130 -0
- package/scripts/verify-docs-parity.sh +28 -28
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
4
5
|
import {
|
|
5
6
|
existsSync,
|
|
6
7
|
lstatSync,
|
|
@@ -20,6 +21,7 @@ const RED_MARKER = "PHASE26_RED:SETUP_CORE_MISSING";
|
|
|
20
21
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
21
22
|
const setupCorePath = join(here, "setup-core.mjs");
|
|
22
23
|
const setupPath = join(here, "setup.mjs");
|
|
24
|
+
const terminalPromptsPath = join(here, "terminal-prompts.mjs");
|
|
23
25
|
const MANAGED_PATHS = [".ai", ".planning", ".agentfs"];
|
|
24
26
|
|
|
25
27
|
function assertNoManagedPaths(target, label) {
|
|
@@ -81,6 +83,10 @@ async function loadSetup() {
|
|
|
81
83
|
return import(pathToFileURL(setupPath).href);
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
async function loadTerminalPrompts() {
|
|
87
|
+
return import(pathToFileURL(terminalPromptsPath).href);
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
function assertExports(core) {
|
|
85
91
|
for (const name of [
|
|
86
92
|
"parseSetupArgs",
|
|
@@ -225,6 +231,126 @@ async function runInteractive(setup, args, responses) {
|
|
|
225
231
|
return { status, prompts, output: output.read(), error: error.read() };
|
|
226
232
|
}
|
|
227
233
|
|
|
234
|
+
class FakeTTY extends EventEmitter {
|
|
235
|
+
constructor() {
|
|
236
|
+
super();
|
|
237
|
+
this.isTTY = true;
|
|
238
|
+
this.isRaw = false;
|
|
239
|
+
this.paused = true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
setRawMode(value) { this.isRaw = value; }
|
|
243
|
+
isPaused() { return this.paused; }
|
|
244
|
+
resume() { this.paused = false; }
|
|
245
|
+
pause() { this.paused = true; }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function testTerminalSelectors(prompts) {
|
|
249
|
+
const input = new FakeTTY();
|
|
250
|
+
const output = captureStream();
|
|
251
|
+
output.stream.isTTY = true;
|
|
252
|
+
assert.equal(prompts.supportsTerminalPrompts(input, output.stream, { TERM: "xterm-256color" }), true);
|
|
253
|
+
assert.equal(prompts.supportsTerminalPrompts(input, output.stream, { TERM: "dumb" }), false);
|
|
254
|
+
|
|
255
|
+
const multi = prompts.selectManyPrompt({
|
|
256
|
+
message: "Coding harnesses",
|
|
257
|
+
choices: [
|
|
258
|
+
{ value: "claude", label: "Claude Code" },
|
|
259
|
+
{ value: "codex", label: "Codex CLI" },
|
|
260
|
+
],
|
|
261
|
+
input,
|
|
262
|
+
output: output.stream,
|
|
263
|
+
});
|
|
264
|
+
setImmediate(() => {
|
|
265
|
+
input.emit("keypress", " ", { name: "space" });
|
|
266
|
+
input.emit("keypress", "", { name: "down" });
|
|
267
|
+
input.emit("keypress", " ", { name: "space" });
|
|
268
|
+
input.emit("keypress", "", { name: "return" });
|
|
269
|
+
});
|
|
270
|
+
assert.deepEqual(await multi, ["claude", "codex"]);
|
|
271
|
+
assert.equal(input.isRaw, false);
|
|
272
|
+
assert.equal(input.isPaused(), true);
|
|
273
|
+
assert.match(output.read(), /\[x\] Claude Code/);
|
|
274
|
+
assert.match(output.read(), /Coding harnesses: Claude Code, Codex CLI/);
|
|
275
|
+
|
|
276
|
+
const single = prompts.selectOnePrompt({
|
|
277
|
+
message: "Memory mode",
|
|
278
|
+
choices: [
|
|
279
|
+
{ value: "local", label: "Local" },
|
|
280
|
+
{ value: "none", label: "None" },
|
|
281
|
+
],
|
|
282
|
+
initialValue: "local",
|
|
283
|
+
input,
|
|
284
|
+
output: output.stream,
|
|
285
|
+
});
|
|
286
|
+
setImmediate(() => {
|
|
287
|
+
input.emit("keypress", "", { name: "down" });
|
|
288
|
+
input.emit("keypress", "", { name: "return" });
|
|
289
|
+
});
|
|
290
|
+
assert.equal(await single, "none");
|
|
291
|
+
assert.match(output.read(), /Memory mode: None/);
|
|
292
|
+
|
|
293
|
+
const cancelled = prompts.selectOnePrompt({
|
|
294
|
+
message: "Cancel me",
|
|
295
|
+
choices: [{ value: "only", label: "Only choice" }],
|
|
296
|
+
input,
|
|
297
|
+
output: output.stream,
|
|
298
|
+
});
|
|
299
|
+
setImmediate(() => input.emit("keypress", "", { name: "escape" }));
|
|
300
|
+
await assert.rejects(cancelled, (error) => error?.status === 130 && /cancelled/i.test(error.message));
|
|
301
|
+
assert.equal(input.isRaw, false);
|
|
302
|
+
assert.equal(input.isPaused(), true);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function testStructuredSetupSelectors(setup, fixture) {
|
|
306
|
+
const selections = [];
|
|
307
|
+
const output = captureStream();
|
|
308
|
+
const error = captureStream();
|
|
309
|
+
const status = await setup.runSetup([fixture.empty], {
|
|
310
|
+
isTTY: true,
|
|
311
|
+
input: { isTTY: true },
|
|
312
|
+
output: output.stream,
|
|
313
|
+
error: error.stream,
|
|
314
|
+
selectOne: async (options) => {
|
|
315
|
+
selections.push(options);
|
|
316
|
+
if (options.message === "Git integration") return "init";
|
|
317
|
+
if (options.message === "Memory mode") return "local";
|
|
318
|
+
return "no";
|
|
319
|
+
},
|
|
320
|
+
selectMany: async (options) => {
|
|
321
|
+
selections.push(options);
|
|
322
|
+
return ["codex", "opencode"];
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
assert.equal(status, 0);
|
|
326
|
+
assert.equal(error.read(), "");
|
|
327
|
+
assert.match(output.read(), /Setup cancelled; no files were changed/);
|
|
328
|
+
assert.deepEqual(selections.map(({ message }) => message), [
|
|
329
|
+
"Git integration",
|
|
330
|
+
"Coding harnesses",
|
|
331
|
+
"Memory mode",
|
|
332
|
+
"Apply this setup plan?",
|
|
333
|
+
]);
|
|
334
|
+
const harnessPrompt = selections.find(({ message }) => message === "Coding harnesses");
|
|
335
|
+
assert.deepEqual(harnessPrompt.choices.map(({ value }) => value), ["claude", "opencode", "pi", "kimi", "qwen", "codex"]);
|
|
336
|
+
assert.match(harnessPrompt.choices.find(({ value }) => value === "codex").label, /Codex CLI/);
|
|
337
|
+
assertNoManagedPaths(fixture.empty, "cancelled structured setup");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function testRedirectedOutputIsNoninteractive(setup, fixture) {
|
|
341
|
+
const output = captureStream();
|
|
342
|
+
const error = captureStream();
|
|
343
|
+
const status = await setup.runSetup([fixture.empty, "--yes"], {
|
|
344
|
+
input: { isTTY: true },
|
|
345
|
+
output: output.stream,
|
|
346
|
+
error: error.stream,
|
|
347
|
+
});
|
|
348
|
+
assert.equal(status, 2);
|
|
349
|
+
assert.equal(output.read(), "");
|
|
350
|
+
assert.match(error.read(), /git|harness|memory|usage/i);
|
|
351
|
+
assertNoManagedPaths(fixture.empty, "redirected setup output");
|
|
352
|
+
}
|
|
353
|
+
|
|
228
354
|
async function testInteractiveGitRecommendation(setup, fixture) {
|
|
229
355
|
const accepted = await runInteractive(setup, [], [fixture.missing, "init", "pi", "local", "yes"]);
|
|
230
356
|
assert.equal(accepted.status, 0);
|
|
@@ -271,6 +397,10 @@ async function main() {
|
|
|
271
397
|
testChoiceContract(core, fixture);
|
|
272
398
|
testPreflightContract(core, fixture);
|
|
273
399
|
const setup = await loadSetup();
|
|
400
|
+
const terminalPrompts = await loadTerminalPrompts();
|
|
401
|
+
await testTerminalSelectors(terminalPrompts);
|
|
402
|
+
await testStructuredSetupSelectors(setup, fixture);
|
|
403
|
+
await testRedirectedOutputIsNoninteractive(setup, fixture);
|
|
274
404
|
await testInteractiveGitRecommendation(setup, fixture);
|
|
275
405
|
console.log("PASS: setup target, Git, syntax, choice, and no-write preflight contract");
|
|
276
406
|
} finally {
|
|
@@ -9,8 +9,8 @@ set -euo pipefail
|
|
|
9
9
|
#
|
|
10
10
|
# 1. Env-key check: every CAIRN_[A-Z_]+/MCP_HTTP_[A-Z_]+ token read in
|
|
11
11
|
# mcp-memory-server/src/*.ts (the runtime server config surface --
|
|
12
|
-
# NOT scripts/ or bin/) must be named somewhere in
|
|
13
|
-
#
|
|
12
|
+
# NOT scripts/ or bin/) must be named somewhere in the versioned
|
|
13
|
+
# top-level docs. `comm -23` on two sorted lists -- code-keys-not-in-
|
|
14
14
|
# docs -- not a symmetric diff.
|
|
15
15
|
# 2. Command check: every claude/commands/*.md basename must appear in
|
|
16
16
|
# docs/operating.md.
|
|
@@ -27,8 +27,8 @@ Usage: verify-docs-parity.sh
|
|
|
27
27
|
verify-docs-parity.sh -h|--help
|
|
28
28
|
|
|
29
29
|
Checks that every CAIRN_*/MCP_HTTP_* env key read by the cairn-memory MCP
|
|
30
|
-
server (mcp-memory-server/src/*.ts) is named in docs
|
|
31
|
-
|
|
30
|
+
server (mcp-memory-server/src/*.ts) is named in the versioned docs, and that
|
|
31
|
+
every claude/commands/*.md command is named in
|
|
32
32
|
docs/operating.md. One-directional: doc-only names are not a failure.
|
|
33
33
|
Prints every missing key/command by name, then exits non-zero on any
|
|
34
34
|
drift, or 0 if the docs are fully in sync with the shipped code.
|
|
@@ -264,7 +264,7 @@ check_env_keys() {
|
|
|
264
264
|
local code_keys doc_keys missing
|
|
265
265
|
|
|
266
266
|
code_keys=$(grep -ohE "\\b${ENV_KEY_PATTERN}\\b" mcp-memory-server/src/*.ts | grep -v '^CAIRN_TEST_' | sort -u)
|
|
267
|
-
doc_keys=$(grep -ohE "\\b${ENV_KEY_PATTERN}\\b" docs
|
|
267
|
+
doc_keys=$(grep -ohE "\\b${ENV_KEY_PATTERN}\\b" docs/*.md | sort -u)
|
|
268
268
|
|
|
269
269
|
missing=$(comm -23 <(printf '%s\n' "$code_keys") <(printf '%s\n' "$doc_keys"))
|
|
270
270
|
|
|
@@ -276,7 +276,7 @@ check_env_keys() {
|
|
|
276
276
|
return 1
|
|
277
277
|
fi
|
|
278
278
|
|
|
279
|
-
echo "[env-keys] OK: every mcp-memory-server/src env key is named in
|
|
279
|
+
echo "[env-keys] OK: every mcp-memory-server/src env key is named in the versioned docs"
|
|
280
280
|
return 0
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -316,12 +316,12 @@ check_typed_contract() {
|
|
|
316
316
|
failed=1
|
|
317
317
|
fi
|
|
318
318
|
done <<'EOF'
|
|
319
|
-
CAIRN_TYPED_MEMORY_NODES|
|
|
320
|
-
memory_import|
|
|
321
|
-
address_space|
|
|
322
|
-
node_types|
|
|
323
|
-
tags_all|
|
|
324
|
-
tags_any|
|
|
319
|
+
CAIRN_TYPED_MEMORY_NODES|docs/operating.md
|
|
320
|
+
memory_import|docs/operating.md docs/privacy-and-data-flow.md
|
|
321
|
+
address_space|docs/operating.md docs/privacy-and-data-flow.md
|
|
322
|
+
node_types|docs/operating.md
|
|
323
|
+
tags_all|docs/operating.md
|
|
324
|
+
tags_any|docs/operating.md
|
|
325
325
|
schemas/memory-node.schema.json|docs/storage.md
|
|
326
326
|
cairn_node_metadata_v1|docs/storage.md
|
|
327
327
|
cairn_node_import_replays_v1|docs/storage.md
|
|
@@ -350,11 +350,11 @@ check_artifact_contract() {
|
|
|
350
350
|
echo "FATAL: artifact default '$key=$value' is missing from env.example" >&2
|
|
351
351
|
failed=1
|
|
352
352
|
}
|
|
353
|
-
grep -qF "$key"
|
|
353
|
+
grep -qF "$key" docs/operating.md || {
|
|
354
354
|
echo "FATAL: artifact env key '$key' is missing from public configuration docs" >&2
|
|
355
355
|
failed=1
|
|
356
356
|
}
|
|
357
|
-
grep -qF "$value"
|
|
357
|
+
grep -qF "$value" docs/operating.md || {
|
|
358
358
|
echo "FATAL: artifact default '$key=$value' is missing from public configuration docs" >&2
|
|
359
359
|
failed=1
|
|
360
360
|
}
|
|
@@ -368,7 +368,7 @@ CAIRN_ARTIFACT_GENERATED_FILE_SNAPSHOT_MAX_BYTES|262144|GENERATED_FILE_MAX_SNAPS
|
|
|
368
368
|
EOF
|
|
369
369
|
|
|
370
370
|
for key in CAIRN_COMPACTION_CAPTURE CAIRN_ARTIFACT_STORE CAIRN_ARTIFACT_HTTP; do
|
|
371
|
-
grep -qF "$key" mcp-memory-server/src/artifact-schema.ts
|
|
371
|
+
grep -qF "$key" mcp-memory-server/src/artifact-schema.ts docs/operating.md docs/privacy-and-data-flow.md || {
|
|
372
372
|
echo "FATAL: artifact feature flag '$key' is not source/docs complete" >&2
|
|
373
373
|
failed=1
|
|
374
374
|
}
|
|
@@ -376,13 +376,13 @@ EOF
|
|
|
376
376
|
|
|
377
377
|
for source_term in artifact_write artifact_read artifact_list artifact_delete; do
|
|
378
378
|
grep -qF "\"$source_term\"" mcp-memory-server/src/index.ts || failed=1
|
|
379
|
-
grep -qF "$source_term"
|
|
379
|
+
grep -qF "$source_term" docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
380
380
|
done
|
|
381
381
|
for source_term in 'list [--kind K] [--session REF] [--json]' 'show <artifact-id-or-prefix> [--json]' 'delete <artifact-id-or-prefix> [--dry-run] [--json]' 'prune [--dry-run] [--include-protected] [--json]'; do
|
|
382
382
|
grep -qF "$source_term" mcp-memory-server/src/artifact-cli.ts || failed=1
|
|
383
383
|
done
|
|
384
384
|
grep -qF '.agentfs/artifacts.db' docs/storage.md docs/privacy-and-data-flow.md || failed=1
|
|
385
|
-
grep -qF 'CAIRN_ARTIFACT_HTTP' mcp-memory-server/src/index.ts
|
|
385
|
+
grep -qF 'CAIRN_ARTIFACT_HTTP' mcp-memory-server/src/index.ts docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
386
386
|
grep -qE 'redact[^.]{0,180}(before|then)[^.]{0,180}(digest|index|write)' docs/privacy-and-data-flow.md || failed=1
|
|
387
387
|
grep -qE 'default uninstall retains|Default uninstall retains' docs/storage.md docs/privacy-and-data-flow.md || failed=1
|
|
388
388
|
grep -qF -- '--purge-memory PROJECT' docs/storage.md || failed=1
|
|
@@ -390,10 +390,10 @@ EOF
|
|
|
390
390
|
|
|
391
391
|
for version in 2.1.219 2.1.220; do
|
|
392
392
|
grep -qF "version: \"$version\"" mcp-memory-server/src/compaction-normalize.ts || failed=1
|
|
393
|
-
grep -qF "$version"
|
|
393
|
+
grep -qF "$version" docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
394
394
|
done
|
|
395
395
|
grep -qF 'version: "1.17.20"' mcp-memory-server/src/compaction-normalize.ts || failed=1
|
|
396
|
-
grep -qF '1.17.20'
|
|
396
|
+
grep -qF '1.17.20' docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
397
397
|
|
|
398
398
|
[[ "$failed" -eq 0 ]] && echo "[artifact-contract] OK: artifact flags/defaults/tools/paths/privacy/uninstall/version pins match source"
|
|
399
399
|
return "$failed"
|
|
@@ -440,8 +440,8 @@ check_work_evidence_contract() {
|
|
|
440
440
|
grep -qF "$key" mcp-memory-server/src/work-evidence-schema.ts || failed=1
|
|
441
441
|
grep -qF "$source_term" mcp-memory-server/src/work-evidence-schema.ts || failed=1
|
|
442
442
|
grep -qxF "# $key=$value" templates/env.example.template || failed=1
|
|
443
|
-
grep -qF "$key"
|
|
444
|
-
grep -qF "$value"
|
|
443
|
+
grep -qF "$key" docs/operating.md docs/work-evidence.md || failed=1
|
|
444
|
+
grep -qF "$value" docs/operating.md docs/work-evidence.md || failed=1
|
|
445
445
|
done <<'EOF'
|
|
446
446
|
CAIRN_WORK_EVIDENCE_RETENTION_DAYS|30|WORK_EVIDENCE_DEFAULT_RETENTION_DAYS = 30
|
|
447
447
|
CAIRN_WORK_EVIDENCE_STORE_MAX_BYTES|67108864|WORK_EVIDENCE_DEFAULT_STORE_MAX_BYTES = 64 * 1024 * 1024
|
|
@@ -450,11 +450,11 @@ CAIRN_WORK_EVIDENCE_PATCH_MAX_BYTES|1048576|WORK_EVIDENCE_DEFAULT_PATCH_MAX_BYTE
|
|
|
450
450
|
EOF
|
|
451
451
|
|
|
452
452
|
for key in CAIRN_WORK_EVIDENCE CAIRN_WORK_EVIDENCE_PATCH; do
|
|
453
|
-
grep -qF "$key" mcp-memory-server/src/work-evidence-schema.ts templates/env.example.template
|
|
453
|
+
grep -qF "$key" mcp-memory-server/src/work-evidence-schema.ts templates/env.example.template docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
454
454
|
done
|
|
455
455
|
for source_term in work_evidence_list work_evidence_read; do
|
|
456
456
|
grep -qF "\"$source_term\"" mcp-memory-server/src/index.ts || failed=1
|
|
457
|
-
grep -qF "$source_term"
|
|
457
|
+
grep -qF "$source_term" docs/operating.md docs/privacy-and-data-flow.md docs/work-evidence.md || failed=1
|
|
458
458
|
done
|
|
459
459
|
for source_term in 'list [--status pending|complete] [--json]' 'show <evidence-id-or-prefix> [--json]' 'delete <evidence-id-or-prefix> [--dry-run] [--json]' 'prune [--dry-run] [--json]' 'doctor [--repair] [--json]'; do
|
|
460
460
|
grep -qF "$source_term" mcp-memory-server/src/work-evidence-cli.ts || failed=1
|
|
@@ -478,7 +478,7 @@ check_capability_contract() {
|
|
|
478
478
|
mcp-memory-server/src/capability-store.ts \
|
|
479
479
|
mcp-memory-server/src/capability-adapter.ts \
|
|
480
480
|
schemas/capability-callback.schema.json \
|
|
481
|
-
docs/operating.md docs/storage.md docs/privacy-and-data-flow.md
|
|
481
|
+
docs/operating.md docs/storage.md docs/privacy-and-data-flow.md; do
|
|
482
482
|
if [[ ! -f "$file" ]]; then
|
|
483
483
|
echo "FATAL: capability parity input is missing: $file" >&2
|
|
484
484
|
failed=1
|
|
@@ -491,11 +491,11 @@ check_capability_contract() {
|
|
|
491
491
|
echo "FATAL: canonical capability ID is missing from source: $id" >&2
|
|
492
492
|
failed=1
|
|
493
493
|
}
|
|
494
|
-
grep -qF "$id"
|
|
494
|
+
grep -qF "$id" docs/operating.md || {
|
|
495
495
|
echo "FATAL: canonical capability ID is undocumented: $id" >&2
|
|
496
496
|
failed=1
|
|
497
497
|
}
|
|
498
|
-
grep -qF "$env"
|
|
498
|
+
grep -qF "$env" docs/operating.md templates/env.example.template || {
|
|
499
499
|
echo "FATAL: capability environment key is not source/docs complete: $env" >&2
|
|
500
500
|
failed=1
|
|
501
501
|
}
|
|
@@ -511,7 +511,7 @@ context.explore|CAIRN_CAPABILITY_CONTEXT_EXPLORE
|
|
|
511
511
|
EOF
|
|
512
512
|
|
|
513
513
|
for term in CAIRN_CAPABILITY_CONTRACT CAIRN_CAPABILITY_LOGGING; do
|
|
514
|
-
grep -qF "$term" mcp-memory-server/src/capability-config.ts
|
|
514
|
+
grep -qF "$term" mcp-memory-server/src/capability-config.ts docs/operating.md templates/env.example.template || {
|
|
515
515
|
echo "FATAL: capability master/logging flag is not source/docs complete: $term" >&2
|
|
516
516
|
failed=1
|
|
517
517
|
}
|
|
@@ -520,7 +520,7 @@ EOF
|
|
|
520
520
|
while IFS='|' read -r id tool; do
|
|
521
521
|
grep -qF "\"$id\"" mcp-memory-server/src/index.ts || failed=1
|
|
522
522
|
grep -qF "\"$tool\"" mcp-memory-server/src/index.ts || failed=1
|
|
523
|
-
grep -qF "$tool"
|
|
523
|
+
grep -qF "$tool" docs/operating.md || {
|
|
524
524
|
echo "FATAL: omitted MCP tool is undocumented: $tool" >&2
|
|
525
525
|
failed=1
|
|
526
526
|
}
|