@cairnkeep/cli 2.13.0 → 2.13.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 +11 -1
- package/docs/learning/CURRICULUM-MAP.md +1 -1
- package/docs/learning/FEATURE-GUIDE.md +1 -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/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 +2 -2
- package/docs/releasing.md +6 -2
- package/examples/eval/bundled-fake.json +1 -1
- package/package.json +5 -2
- package/scripts/test-release-sbom.sh +91 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable user-facing changes are documented here. This project follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.13.1] - 2026-08-17
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Generate release SBOMs directly as reproducible, schema-validated CycloneDX
|
|
13
|
+
1.6 documents with the pinned official npm generator. This replaces npm's
|
|
14
|
+
invalid CycloneDX 1.5 output and fails publication before attaching a broken
|
|
15
|
+
SBOM.
|
|
16
|
+
|
|
8
17
|
## [2.13.0] - 2026-08-17
|
|
9
18
|
|
|
10
19
|
### Added
|
|
@@ -347,7 +356,8 @@ stored memories.
|
|
|
347
356
|
- Preserve executable permissions and Bash 3.2 portability on macOS.
|
|
348
357
|
- Add backup-first uninstall and SQLite-safe memory export/import guidance.
|
|
349
358
|
|
|
350
|
-
[Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.13.
|
|
359
|
+
[Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.13.1...HEAD
|
|
360
|
+
[2.13.1]: https://github.com/cairnkeep/cairnkeep/compare/v2.13.0...v2.13.1
|
|
351
361
|
[2.13.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.12.0...v2.13.0
|
|
352
362
|
[2.12.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.11.0...v2.12.0
|
|
353
363
|
[2.11.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.9.0...v2.11.0
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Status:** Ready
|
|
4
4
|
**Track:** Quickstart
|
|
5
5
|
**Time:** 15 minutes
|
|
6
|
-
**Tested with:** Cairnkeep 2.13.
|
|
6
|
+
**Tested with:** Cairnkeep 2.13.1
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
9
|
|
|
@@ -26,7 +26,7 @@ ephemeral; the named course volume is persistent.
|
|
|
26
26
|
1. Pull the public image:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
podman pull ghcr.io/cairnkeep/cairnkeep:2.13.
|
|
29
|
+
podman pull ghcr.io/cairnkeep/cairnkeep:2.13.1
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
2. Start the stdio server with a read-only root filesystem and no Linux
|
|
@@ -38,7 +38,7 @@ ephemeral; the named course volume is persistent.
|
|
|
38
38
|
--read-only --cap-drop=all --security-opt=no-new-privileges \
|
|
39
39
|
--tmpfs=/tmp:rw,noexec,nosuid,size=64m,mode=1777 \
|
|
40
40
|
--volume cairnkeep-course-data:/data:Z,U \
|
|
41
|
-
ghcr.io/cairnkeep/cairnkeep:2.13.
|
|
41
|
+
ghcr.io/cairnkeep/cairnkeep:2.13.1 stdio
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
3. The process waits quietly for MCP input. Press `Ctrl-C` after confirming it
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Status:** Ready
|
|
4
4
|
**Track:** Quickstart
|
|
5
5
|
**Time:** 25 minutes
|
|
6
|
-
**Tested with:** Cairnkeep 2.13.
|
|
6
|
+
**Tested with:** Cairnkeep 2.13.1 and Node.js 22 or newer
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
9
|
|
|
@@ -47,7 +47,7 @@ sync. Codex project trust remains an explicit operator decision.
|
|
|
47
47
|
2. Install Cairnkeep:
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
npm install --global @cairnkeep/cli@2.13.
|
|
50
|
+
npm install --global @cairnkeep/cli@2.13.1
|
|
51
51
|
cairn --version
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Status:** Ready
|
|
4
4
|
**Track:** Operator
|
|
5
5
|
**Time:** 30 minutes
|
|
6
|
-
**Tested with:** Cairnkeep 2.13.
|
|
6
|
+
**Tested with:** Cairnkeep 2.13.1, Windows x64, and Node.js 22 or newer
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
9
|
|
|
@@ -16,7 +16,7 @@ perform a reversible uninstall without relying on WSL or Git Bash.
|
|
|
16
16
|
From PowerShell:
|
|
17
17
|
|
|
18
18
|
```powershell
|
|
19
|
-
npm install --global @cairnkeep/cli@2.13.
|
|
19
|
+
npm install --global @cairnkeep/cli@2.13.1
|
|
20
20
|
cairn version
|
|
21
21
|
|
|
22
22
|
$Lab = Join-Path $env:TEMP 'Cairnkeep Windows Lab'
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Status:** Ready
|
|
4
4
|
**Track:** Quickstart and Operator
|
|
5
5
|
**Time:** 35 minutes
|
|
6
|
-
**Tested with:** Cairnkeep 2.13.
|
|
6
|
+
**Tested with:** Cairnkeep 2.13.1; Pi 0.84.1; Node.js 22, 24, and 26
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
9
|
|
|
@@ -134,7 +134,7 @@ prompts, approve skills, create an autonomous loop, or add remote access.
|
|
|
134
134
|
|
|
135
135
|
## Release boundary
|
|
136
136
|
|
|
137
|
-
Ready means this lesson and its deterministic contracts pass against v2.13.
|
|
137
|
+
Ready means this lesson and its deterministic contracts pass against v2.13.1.
|
|
138
138
|
Release readiness requires a non-skipping acceptance run
|
|
139
139
|
against separate Pi 0.84.1 minimum and explicitly versioned registry-current
|
|
140
140
|
installations, plus the documented Node.js, Bash 3.2, and native Windows matrix.
|
package/docs/releasing.md
CHANGED
|
@@ -12,8 +12,12 @@ from a workstation.
|
|
|
12
12
|
4. Publish a GitHub Release whose tag is exactly `v<package.json version>`.
|
|
13
13
|
5. The `Publish release` workflow checks out that immutable tag, repeats the
|
|
14
14
|
tests, and publishes `@cairnkeep/cli` with npm provenance.
|
|
15
|
-
6. The workflow
|
|
16
|
-
to
|
|
15
|
+
6. The workflow uses the exact locked `@cyclonedx/cyclonedx-npm` development
|
|
16
|
+
dependency to generate a reproducible CycloneDX 1.6 SBOM from
|
|
17
|
+
`package-lock.json`. Generation includes schema validation and runs before
|
|
18
|
+
npm publication, so an invalid document blocks both publication and
|
|
19
|
+
attachment. The workflow attaches that SBOM, the npm tarball, and SHA-256
|
|
20
|
+
checksums to the GitHub Release.
|
|
17
21
|
7. The workflow publishes versioned `linux/amd64` and `linux/arm64`
|
|
18
22
|
memory-server and workspace images to GHCR. It also updates `latest` for a
|
|
19
23
|
stable release or `next` for a prerelease.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"identifier":"cairn-offline-fake-v1","package_version":"2.13.
|
|
1
|
+
{"identifier":"cairn-offline-fake-v1","package_version":"2.13.1","schema_version":1,"task_set_digest":"13e95379f35839593047ec34521a65a8fa3ab92bd3993bca9dc8bf5c4a9ff36f"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cairnkeep/cli",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.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",
|
|
@@ -70,5 +70,8 @@
|
|
|
70
70
|
"pi-coding-agent",
|
|
71
71
|
"context",
|
|
72
72
|
"rag"
|
|
73
|
-
]
|
|
73
|
+
],
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@cyclonedx/cyclonedx-npm": "6.0.0"
|
|
76
|
+
}
|
|
74
77
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
5
|
+
WORKFLOW="$ROOT/.github/workflows/publish.yml"
|
|
6
|
+
TEMP_DIR=$(mktemp -d)
|
|
7
|
+
trap 'rm -f "$TEMP_DIR/cairnkeep.cdx.json"; rmdir "$TEMP_DIR"' EXIT
|
|
8
|
+
|
|
9
|
+
fail() {
|
|
10
|
+
echo "FAIL: $*" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
node - "$ROOT/package.json" "$ROOT/package-lock.json" "$WORKFLOW" <<'NODE'
|
|
15
|
+
const fs = require("node:fs");
|
|
16
|
+
|
|
17
|
+
const manifest = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
18
|
+
const lock = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
|
|
19
|
+
const workflow = fs.readFileSync(process.argv[4], "utf8");
|
|
20
|
+
const expected = "6.0.0";
|
|
21
|
+
|
|
22
|
+
if (manifest.devDependencies?.["@cyclonedx/cyclonedx-npm"] !== expected) {
|
|
23
|
+
throw new Error(`package.json must pin @cyclonedx/cyclonedx-npm exactly to ${expected}`);
|
|
24
|
+
}
|
|
25
|
+
if (lock.packages?.[""]?.devDependencies?.["@cyclonedx/cyclonedx-npm"] !== expected) {
|
|
26
|
+
throw new Error(`package-lock.json root must pin @cyclonedx/cyclonedx-npm exactly to ${expected}`);
|
|
27
|
+
}
|
|
28
|
+
if (lock.packages?.["node_modules/@cyclonedx/cyclonedx-npm"]?.version !== expected) {
|
|
29
|
+
throw new Error(`package-lock.json must resolve @cyclonedx/cyclonedx-npm ${expected}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const artifactStep = workflow.indexOf("- name: Build and validate release artifacts");
|
|
33
|
+
const publishStep = workflow.indexOf("- name: Publish to npm with provenance");
|
|
34
|
+
const attachStep = workflow.indexOf("- name: Attach package and SBOM to GitHub release");
|
|
35
|
+
if (artifactStep < 0 || publishStep < 0 || attachStep < 0 || !(artifactStep < publishStep && publishStep < attachStep)) {
|
|
36
|
+
throw new Error("release artifacts must validate before npm publication and attach only after publication");
|
|
37
|
+
}
|
|
38
|
+
NODE
|
|
39
|
+
|
|
40
|
+
grep -Fq './node_modules/.bin/cyclonedx-npm' "$WORKFLOW" || fail "release workflow does not use the locked CycloneDX generator"
|
|
41
|
+
grep -Fq -- '--package-lock-only' "$WORKFLOW" || fail "release workflow does not use the package lock"
|
|
42
|
+
grep -Fq -- '--omit dev' "$WORKFLOW" || fail "release workflow does not omit development dependencies"
|
|
43
|
+
grep -Fq -- '--output-reproducible' "$WORKFLOW" || fail "release workflow does not request reproducible output"
|
|
44
|
+
grep -Fq -- '--spec-version 1.6' "$WORKFLOW" || fail "release workflow does not emit CycloneDX 1.6"
|
|
45
|
+
grep -Fq -- '--validate' "$WORKFLOW" || fail "release workflow does not validate the generated SBOM"
|
|
46
|
+
if grep -Fq 'npm sbom' "$WORKFLOW"; then
|
|
47
|
+
fail "release workflow still uses npm sbom"
|
|
48
|
+
fi
|
|
49
|
+
if grep -Fqi 'sbomsmith' "$WORKFLOW"; then
|
|
50
|
+
fail "release workflow must not depend on an external converter"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
"$ROOT/node_modules/.bin/cyclonedx-npm" \
|
|
54
|
+
--package-lock-only \
|
|
55
|
+
--omit dev \
|
|
56
|
+
--output-reproducible \
|
|
57
|
+
--spec-version 1.6 \
|
|
58
|
+
--output-format JSON \
|
|
59
|
+
--output-file "$TEMP_DIR/cairnkeep.cdx.json" \
|
|
60
|
+
--validate \
|
|
61
|
+
"$ROOT/package.json"
|
|
62
|
+
|
|
63
|
+
node - "$TEMP_DIR/cairnkeep.cdx.json" <<'NODE'
|
|
64
|
+
const fs = require("node:fs");
|
|
65
|
+
|
|
66
|
+
const bom = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
67
|
+
if (bom.bomFormat !== "CycloneDX") throw new Error("release SBOM format is not CycloneDX");
|
|
68
|
+
if (bom.specVersion !== "1.6") throw new Error("release SBOM version is not CycloneDX 1.6");
|
|
69
|
+
if (!Array.isArray(bom.components) || bom.components.length === 0) throw new Error("release SBOM has no components");
|
|
70
|
+
if (!Array.isArray(bom.dependencies) || bom.dependencies.length === 0) throw new Error("release SBOM has no dependency graph");
|
|
71
|
+
|
|
72
|
+
const scpLike = /^[^/@\s:]+@[^/\s:]+:.+$/;
|
|
73
|
+
function inspect(value) {
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
for (const item of value) inspect(item);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!value || typeof value !== "object") return;
|
|
79
|
+
if (Array.isArray(value.externalReferences)) {
|
|
80
|
+
for (const reference of value.externalReferences) {
|
|
81
|
+
if (typeof reference?.url === "string" && scpLike.test(reference.url)) {
|
|
82
|
+
throw new Error(`release SBOM contains an invalid SCP-style external reference: ${reference.url}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const child of Object.values(value)) inspect(child);
|
|
87
|
+
}
|
|
88
|
+
inspect(bom);
|
|
89
|
+
NODE
|
|
90
|
+
|
|
91
|
+
echo "PASS: release SBOM is reproducible, schema-validated CycloneDX 1.6"
|