@cairnkeep/cli 2.22.5 → 2.23.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 +43 -0
- package/claude/commands/recall.md +8 -1
- package/docs/README.md +3 -0
- package/docs/agents.md +9 -0
- package/docs/benchmarks/memory-retrieval-v1.json +115 -0
- package/docs/domain-knowledge.md +42 -4
- package/docs/learning/CURRICULUM-MAP.md +3 -3
- package/docs/learning/FEATURE-GUIDE.md +7 -1
- package/docs/learning/lessons/L00-why-cairnkeep.md +1 -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 +1 -1
- package/docs/learning/lessons/L04-memory-fundamentals.md +20 -0
- package/docs/learning/lessons/L09-domain-knowledge.md +11 -0
- 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 +1 -1
- package/docs/learning/lessons/L24-okf-exchange.md +1 -1
- package/docs/learning/lessons/L25-playbooks.md +1 -1
- package/docs/learning/lessons/L26-context-intelligence.md +1 -1
- package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
- package/docs/learning/lessons/L28-security-assurance.md +2 -2
- package/docs/learning/video-scripts/V04-memory-retrieval-supplement.md +34 -0
- package/docs/learning/video-scripts/V09-workspace-routing-supplement.md +36 -0
- package/docs/memory-retrieval.md +174 -0
- package/docs/operating.md +5 -0
- package/docs/releasing.md +7 -0
- package/docs/storage.md +5 -0
- package/examples/eval/bundled-fake.json +1 -1
- package/mcp-memory-server/dist/anythingllm-workspace.js +109 -0
- package/mcp-memory-server/dist/index.js +54 -6
- package/mcp-memory-server/dist/memory-retrieval.js +128 -0
- package/mcp-memory-server/package.json +5 -2
- package/opencode/command/recall.md +8 -1
- package/package.json +2 -2
- package/scripts/test-dependency-security.mjs +40 -0
- package/scripts/test-dependency-security.sh +4 -0
- package/scripts/test-memory-retrieval-docs.sh +19 -0
- package/scripts/test-release-sbom.sh +32 -0
- package/scripts/verify-dependency-security.mjs +19 -0
- package/templates/playbook-agent-instructions.md.template +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cairnkeep/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.1",
|
|
4
4
|
"description": "A durable, harness-agnostic memory + context layer for coding agents and MCP clients.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
42
42
|
"agentfs-sdk": "^0.6.4",
|
|
43
43
|
"yaml": "2.9.1",
|
|
44
|
-
"zod": "^4.6.
|
|
44
|
+
"zod": "^4.6.7"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build:server": "npm --prefix mcp-memory-server ci --include=dev --global=false --dry-run=false && npm --prefix mcp-memory-server run build --global=false --dry-run=false",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const root = fileURLToPath(new URL("../", import.meta.url));
|
|
9
|
+
const policy = join(root, "scripts/verify-dependency-security.mjs");
|
|
10
|
+
const paths = ["package.json", "package-lock.json", "mcp-memory-server/package.json", "mcp-memory-server/package-lock.json"];
|
|
11
|
+
const baseline = Object.fromEntries(paths.map(path => [path, JSON.parse(readFileSync(join(root, path), "utf8"))]));
|
|
12
|
+
const scratch = mkdtempSync(join(tmpdir(), "cairn-dependency-policy-"));
|
|
13
|
+
function check(label, mutate, expected) {
|
|
14
|
+
const values = structuredClone(baseline);
|
|
15
|
+
mutate(values);
|
|
16
|
+
for (const path of paths) {
|
|
17
|
+
const target = join(scratch, path);
|
|
18
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
19
|
+
writeFileSync(target, JSON.stringify(values[path]));
|
|
20
|
+
}
|
|
21
|
+
const result = spawnSync(process.execPath, [policy], { cwd: scratch, encoding: "utf8", timeout: 10000 });
|
|
22
|
+
assert.ifError(result.error);
|
|
23
|
+
assert.equal(result.status, expected ? 1 : 0, `${label}: ${result.stderr}`);
|
|
24
|
+
if (expected) assert.match(result.stderr, expected, label);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
check("valid dependency graph", () => {}, null);
|
|
28
|
+
check("root manifest/lock mismatch", x => { x["package.json"].dependencies.zod = "^4.0.0"; }, /manifest and lock root Zod ranges differ/);
|
|
29
|
+
check("server manifest/lock mismatch", x => { x["mcp-memory-server/package-lock.json"].packages[""].dependencies.zod = "^4.0.0"; }, /manifest and lock root Zod ranges differ/);
|
|
30
|
+
check("matched server range differs from packaged CLI", x => {
|
|
31
|
+
x["mcp-memory-server/package.json"].dependencies.zod = "^4.0.0";
|
|
32
|
+
x["mcp-memory-server/package-lock.json"].packages[""].dependencies.zod = "^4.0.0";
|
|
33
|
+
}, /root and server Zod ranges differ/);
|
|
34
|
+
check("different resolved schema version", x => { x["mcp-memory-server/package-lock.json"].packages["node_modules/zod"].version = "4.0.0"; }, /root and server resolved Zod versions differ/);
|
|
35
|
+
check("missing direct schema", x => { delete x["package-lock.json"].packages["node_modules/zod"]; }, /direct Zod: version must be a string/);
|
|
36
|
+
check("existing SDK advisory check retained", x => { x["package-lock.json"].packages["node_modules/@modelcontextprotocol/sdk"].version = "1.0.0"; }, /resolved @modelcontextprotocol\/sdk: requires/);
|
|
37
|
+
console.log("PASS: dependency policy positive control, schema parity mutations and SDK advisory rejection");
|
|
38
|
+
} finally {
|
|
39
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
40
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
4
|
+
cd "$ROOT"
|
|
5
|
+
for path in docs/memory-retrieval.md docs/benchmarks/memory-retrieval-v1.json docs/learning/video-scripts/V04-memory-retrieval-supplement.md; do
|
|
6
|
+
[[ -f "$path" ]]
|
|
7
|
+
done
|
|
8
|
+
for path in docs/README.md docs/operating.md docs/storage.md docs/agents.md docs/learning/lessons/L04-memory-fundamentals.md; do
|
|
9
|
+
grep -qF 'memory-retrieval.md' "$path"
|
|
10
|
+
done
|
|
11
|
+
for path in claude/commands/recall.md opencode/command/recall.md; do
|
|
12
|
+
grep -qF 'do not automatically widen' "$path"
|
|
13
|
+
grep -qF 'strategy: "hybrid"' "$path"
|
|
14
|
+
done
|
|
15
|
+
grep -qF 'same admission' docs/memory-retrieval.md
|
|
16
|
+
grep -qF 'HTTP 502' docs/memory-retrieval.md
|
|
17
|
+
grep -qF 'ranking-not-confidence' docs/memory-retrieval.md
|
|
18
|
+
grep -qF 'Status: available since Cairnkeep 2.23.0' docs/memory-retrieval.md
|
|
19
|
+
echo 'PASS: hybrid retrieval, Markdown parity, provider limitations and learning/recording references'
|
|
@@ -14,6 +14,8 @@ fail() {
|
|
|
14
14
|
|
|
15
15
|
node - "$ROOT/package.json" "$ROOT/package-lock.json" "$CANDIDATE_WORKFLOW" "$PUBLISH_WORKFLOW" <<'NODE'
|
|
16
16
|
const fs = require("node:fs");
|
|
17
|
+
const assert = require("node:assert/strict");
|
|
18
|
+
const { parse } = require("yaml");
|
|
17
19
|
|
|
18
20
|
const manifest = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
19
21
|
const lock = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
|
|
@@ -36,6 +38,36 @@ if (!candidateWorkflow.includes("release-candidate:")
|
|
|
36
38
|
|| !candidateWorkflow.includes("name: release-candidate-${{ steps.candidate.outputs.tree }}")) {
|
|
37
39
|
throw new Error("CI must build and upload a tree-addressed release candidate after its required jobs");
|
|
38
40
|
}
|
|
41
|
+
function validateCandidateRetry(workflow) {
|
|
42
|
+
const job = workflow.jobs["release-candidate"];
|
|
43
|
+
assert.deepEqual(new Set(job.needs), new Set([
|
|
44
|
+
"memory-server", "native-windows", "repository", "shell-portability",
|
|
45
|
+
"server-boot", "container", "fresh-install-macos",
|
|
46
|
+
]), "candidate retry must retain every platform/runtime gate");
|
|
47
|
+
assert.equal(job.if, undefined, "candidate retry must not bypass failed or pending gates");
|
|
48
|
+
const uploads = job.steps.filter(step => step.uses?.startsWith("actions/upload-artifact@"));
|
|
49
|
+
assert.equal(uploads.length, 1, "candidate retry must have one artifact publication");
|
|
50
|
+
assert.equal(uploads[0].with.name, "release-candidate-${{ steps.candidate.outputs.tree }}");
|
|
51
|
+
assert.equal(uploads[0].with.overwrite, true, "candidate upload must support same-run retries");
|
|
52
|
+
assert.equal(uploads[0].with["if-no-files-found"], "error");
|
|
53
|
+
}
|
|
54
|
+
const candidate = parse(candidateWorkflow);
|
|
55
|
+
const positive = structuredClone(candidate);
|
|
56
|
+
positive.jobs["release-candidate"].steps.find(step => step.uses?.startsWith("actions/upload-artifact@")).with.overwrite = true;
|
|
57
|
+
validateCandidateRetry(positive);
|
|
58
|
+
for (const overwrite of [undefined, false]) {
|
|
59
|
+
const invalid = structuredClone(positive);
|
|
60
|
+
invalid.jobs["release-candidate"].steps.find(step => step.uses?.startsWith("actions/upload-artifact@")).with.overwrite = overwrite;
|
|
61
|
+
assert.throws(() => validateCandidateRetry(invalid), /same-run retries/);
|
|
62
|
+
}
|
|
63
|
+
const missingGate = structuredClone(positive);
|
|
64
|
+
missingGate.jobs["release-candidate"].needs = missingGate.jobs["release-candidate"].needs.filter(name => name !== "native-windows");
|
|
65
|
+
assert.throws(() => validateCandidateRetry(missingGate), /every platform\/runtime gate/);
|
|
66
|
+
const bypass = structuredClone(positive);
|
|
67
|
+
bypass.jobs["release-candidate"].if = "always()";
|
|
68
|
+
assert.throws(() => validateCandidateRetry(bypass), /must not bypass/);
|
|
69
|
+
validateCandidateRetry(candidate);
|
|
70
|
+
console.log("PASS: retry-safe candidate upload and four gate/replacement mutation controls");
|
|
39
71
|
if (!publishWorkflow.includes("- name: Download the CI-verified release candidate")
|
|
40
72
|
|| !publishWorkflow.includes("jq -r '.conclusion'")
|
|
41
73
|
|| !publishWorkflow.includes("== success")
|
|
@@ -130,8 +130,27 @@ function verifyPackageRoot({ label, directory }) {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
function verifySchemaDependencyParity() {
|
|
134
|
+
let expectedRange;
|
|
135
|
+
let expectedVersion;
|
|
136
|
+
for (const { label, directory } of PACKAGE_ROOTS) {
|
|
137
|
+
const manifest = readJson(`${directory}/package.json`);
|
|
138
|
+
const lock = readJson(`${directory}/package-lock.json`);
|
|
139
|
+
const range = manifest.dependencies?.zod;
|
|
140
|
+
assert.equal(typeof range, "string", `${label}: direct Zod range is missing`);
|
|
141
|
+
assert.equal(lock.packages?.[""].dependencies?.zod, range, `${label}: manifest and lock root Zod ranges differ`);
|
|
142
|
+
const version = lock.packages?.["node_modules/zod"]?.version;
|
|
143
|
+
parseVersion(version, `${label} direct Zod`);
|
|
144
|
+
expectedRange ??= range;
|
|
145
|
+
expectedVersion ??= version;
|
|
146
|
+
assert.equal(range, expectedRange, "root and server Zod ranges differ");
|
|
147
|
+
assert.equal(version, expectedVersion, "root and server resolved Zod versions differ");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
try {
|
|
134
152
|
PACKAGE_ROOTS.forEach(verifyPackageRoot);
|
|
153
|
+
verifySchemaDependencyParity();
|
|
135
154
|
process.stdout.write("PASS: dependency manifests and lock graphs exclude known MCP transport advisories\n");
|
|
136
155
|
} catch (error) {
|
|
137
156
|
process.stderr.write(`FAIL: dependency security policy: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -10,6 +10,11 @@ inventory, or search commands such as `pwd`, `ls`, `rg --files`, `find`,
|
|
|
10
10
|
- If `memory_search` is available, derive one short query from the task and
|
|
11
11
|
search `scope: project` once. Derive it from the user's task; do not inspect
|
|
12
12
|
the repository first merely to formulate the query.
|
|
13
|
+
- When the discovered schema supports hybrid search, use it for explicit
|
|
14
|
+
identifiers or mixed wording; request explanations when needed. Supply only
|
|
15
|
+
aliases already known from the task. Hybrid semantic-only matches require an
|
|
16
|
+
explicit model-specific positive similarity threshold; otherwise keep the
|
|
17
|
+
existing semantic strategy for paraphrases. Empty results are acceptable.
|
|
13
18
|
- Treat returned memory as a locator, not authority. Read and verify the
|
|
14
19
|
maintained repository sources it references before implementing a result.
|
|
15
20
|
- If the tool is unavailable or no relevant result exists, continue with
|