@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/claude/commands/recall.md +8 -1
  3. package/docs/README.md +3 -0
  4. package/docs/agents.md +9 -0
  5. package/docs/benchmarks/memory-retrieval-v1.json +115 -0
  6. package/docs/domain-knowledge.md +42 -4
  7. package/docs/learning/CURRICULUM-MAP.md +3 -3
  8. package/docs/learning/FEATURE-GUIDE.md +7 -1
  9. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  10. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  11. package/docs/learning/lessons/L02-installation.md +2 -2
  12. package/docs/learning/lessons/L03-first-project.md +1 -1
  13. package/docs/learning/lessons/L04-memory-fundamentals.md +20 -0
  14. package/docs/learning/lessons/L09-domain-knowledge.md +11 -0
  15. package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
  16. package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
  17. package/docs/learning/lessons/L21-context-packs.md +1 -1
  18. package/docs/learning/lessons/L22-native-windows.md +2 -2
  19. package/docs/learning/lessons/L23-guided-setup.md +1 -1
  20. package/docs/learning/lessons/L24-okf-exchange.md +1 -1
  21. package/docs/learning/lessons/L25-playbooks.md +1 -1
  22. package/docs/learning/lessons/L26-context-intelligence.md +1 -1
  23. package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
  24. package/docs/learning/lessons/L28-security-assurance.md +2 -2
  25. package/docs/learning/video-scripts/V04-memory-retrieval-supplement.md +34 -0
  26. package/docs/learning/video-scripts/V09-workspace-routing-supplement.md +36 -0
  27. package/docs/memory-retrieval.md +174 -0
  28. package/docs/operating.md +5 -0
  29. package/docs/releasing.md +7 -0
  30. package/docs/storage.md +5 -0
  31. package/examples/eval/bundled-fake.json +1 -1
  32. package/mcp-memory-server/dist/anythingllm-workspace.js +109 -0
  33. package/mcp-memory-server/dist/index.js +54 -6
  34. package/mcp-memory-server/dist/memory-retrieval.js +128 -0
  35. package/mcp-memory-server/package.json +5 -2
  36. package/opencode/command/recall.md +8 -1
  37. package/package.json +2 -2
  38. package/scripts/test-dependency-security.mjs +40 -0
  39. package/scripts/test-dependency-security.sh +4 -0
  40. package/scripts/test-memory-retrieval-docs.sh +19 -0
  41. package/scripts/test-release-sbom.sh +32 -0
  42. package/scripts/verify-dependency-security.mjs +19 -0
  43. 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.22.5",
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.5"
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,4 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
4
+ node "$ROOT/scripts/test-dependency-security.mjs"
@@ -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