@cairnkeep/cli 2.4.0 → 2.5.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +12 -1
  3. package/docs/building-an-overlay.md +4 -3
  4. package/docs/harness-compatibility.md +117 -0
  5. package/docs/learning/CURRICULUM-MAP.md +91 -0
  6. package/docs/learning/FEATURE-GUIDE.md +86 -0
  7. package/docs/learning/PRODUCTION-PLAN.md +45 -14
  8. package/docs/learning/README.md +20 -1
  9. package/docs/learning/instructor-guide.md +6 -0
  10. package/docs/learning/lessons/L00-why-cairnkeep.md +20 -8
  11. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  12. package/docs/learning/lessons/L02-installation.md +23 -5
  13. package/docs/learning/lessons/L03-first-project.md +7 -2
  14. package/docs/learning/lessons/L04-memory-fundamentals.md +4 -0
  15. package/docs/learning/lessons/L05-daily-workflow.md +4 -0
  16. package/docs/learning/lessons/L07-privacy-and-storage.md +3 -0
  17. package/docs/learning/lessons/L10-context-exploration.md +4 -2
  18. package/docs/learning/lessons/L12-managed-overlays.md +2 -0
  19. package/docs/learning/lessons/L13-session-evidence.md +71 -0
  20. package/docs/learning/lessons/L14-typed-memory.md +51 -0
  21. package/docs/learning/lessons/L15-capability-governance.md +56 -0
  22. package/docs/learning/lessons/L16-evaluation.md +67 -0
  23. package/docs/learning/lessons/L17-agent-boundary.md +55 -0
  24. package/docs/learning/templates/lesson-template.md +4 -1
  25. package/docs/learning/tracks/evidence-and-evaluation.md +20 -0
  26. package/docs/learning/tracks/operator.md +8 -2
  27. package/docs/learning/tracks/practitioner.md +6 -1
  28. package/docs/learning/tracks/quickstart.md +3 -0
  29. package/docs/learning/video-scripts/L00-why-cairnkeep.md +21 -17
  30. package/docs/learning/video-scripts/L02-installation.md +9 -2
  31. package/docs/learning/video-scripts/L03-first-project.md +3 -0
  32. package/docs/operating.md +38 -5
  33. package/examples/eval/bundled-fake.json +1 -1
  34. package/package.json +1 -1
  35. package/scripts/bootstrap.sh +2 -0
  36. package/scripts/test-bootstrap-untracked.sh +2 -1
  37. package/scripts/test-launcher-hooks.sh +29 -1
  38. package/scripts/test-learning-docs.sh +20 -4
  39. package/scripts/test-package-install.sh +3 -0
  40. package/scripts/uninstall.sh +1 -0
  41. package/templates/start-kimi.sh.template +39 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  ## Recording setup
7
7
 
8
- - Show only the four-layer table from the lesson.
8
+ - Show only the trust-ladder table from the lesson.
9
9
  - Prepare one slide containing the question: “What should survive this session?”
10
10
  - No terminal or private project is required.
11
11
 
@@ -24,12 +24,14 @@ remember, and what must remain in the repository or another canonical system.”
24
24
 
25
25
  **Show:** The lesson outcome.
26
26
 
27
- ## 01:00 - Four layers
27
+ ## 01:00 - Trust ladder
28
28
 
29
- **Say:** “Start with four different layers. The current conversation is working
30
- context. Cairnkeep memory is a set of concise operational facts. The project
31
- wiki is derived, cited knowledge. Code, tests, specifications, policies, and
32
- issue trackers remain canonical.”
29
+ **Say:** “Follow information through a trust ladder. The current conversation is
30
+ working context. Optional captured evidence and hindsight notes preserve bounded
31
+ local observations, but they are not automatically trusted memory. Cairnkeep
32
+ memory holds concise operational facts. The project wiki is derived, cited
33
+ knowledge. Code, tests, specifications, policies, and issue trackers remain
34
+ canonical.”
33
35
 
34
36
  **Show:** Reveal one table row at a time.
35
37
 
@@ -38,10 +40,11 @@ agent find and apply knowledge; it does not redefine truth.”
38
40
 
39
41
  ## 02:40 - Memory versus RAG
40
42
 
41
- **Say:** “Document RAG answers a different question: which parts of a larger
42
- document are relevant right now? Cairnkeep can integrate with a RAG system, but
43
- it does not require one. A decision such as ‘use transactional migrations’ is a
44
- memory. The complete migration policy remains a document.”
43
+ **Say:** “Document RAG and codebase exploration answer a different question:
44
+ which source material is relevant right now? Cairnkeep can integrate with those
45
+ retrieval paths, but it does not require them. A decision such as ‘use
46
+ transactional migrations’ is a memory. The complete migration policy remains a
47
+ document.”
45
48
 
46
49
  **Show:** Memory on the left, document retrieval on the right.
47
50
 
@@ -57,20 +60,21 @@ until confirmed. A verified operational prohibition is a durable constraint.”
57
60
  ## 05:10 - Boundary
58
61
 
59
62
  **Say:** “Cairnkeep does not store every sentence from a session. The default
60
- stdio setup stores memory on this computer. Remote storage, model extraction,
61
- embeddings, and document RAG are all explicit, optional choices.”
63
+ stdio setup stores memory on this computer. Structured capture, notes,
64
+ artifacts, evaluation, remote storage, model extraction, embeddings, and
65
+ document RAG are separate explicit choices.”
62
66
 
63
67
  **Show:** “Local by default. Optional means opt-in.”
64
68
 
65
69
  ## 06:10 - Recap
66
70
 
67
- **Say:** “Remember three things: durable memory is selective, derived knowledge
68
- does not overrule canonical sources, and optional services are not prerequisites.
69
- In L01 we will inspect the published container without installing the complete
70
- workflow.”
71
+ **Say:** “Remember three things: durable memory is selective, evidence is not
72
+ automatically trusted memory, and canonical sources still win. Optional layers
73
+ are not prerequisites. In L01 we will inspect the published container without
74
+ installing the complete workflow.”
71
75
 
72
76
  ## Editing notes
73
77
 
74
- - Chapters: Problem, Four layers, Memory vs RAG, Exercise, Boundary.
78
+ - Chapters: Problem, Trust ladder, Memory vs retrieval, Exercise, Boundary.
75
79
  - Link L00 and L01 in the description.
76
80
  - Re-record the version-independent slide only if the conceptual model changes.
@@ -71,13 +71,20 @@ the harness after applying assets.”
71
71
 
72
72
  **Show:** The two verification commands together.
73
73
 
74
- ## 09:40 - Boundary
74
+ ## 09:30 - Optional shell completion
75
+
76
+ **Do:** Run `source <(cairn completion bash)` and complete `cairn tra`.
77
+
78
+ **Say:** “Completion is optional convenience. It does not start another server
79
+ or enable any feature.”
80
+
81
+ ## 10:00 - Boundary
75
82
 
76
83
  **Say:** “This default server is local. We did not configure embeddings,
77
84
  document RAG, or remote HTTP memory. Those choices have separate lessons
78
85
  because they change data flow.”
79
86
 
80
- ## 10:30 - Recap
87
+ ## 11:00 - Recap
81
88
 
82
89
  **Say:** “The machine setup has three pieces: CLI, MCP registration, and the
83
90
  operating layer. In L03 we add the fourth piece, a bootstrapped project, and
@@ -43,6 +43,9 @@ does not reinstall the machine-level MCP server or operating assets.”
43
43
  **Say:** “Optional unconfigured integrations may be skipped. A configured but
44
44
  unreachable dependency is a failure and should be fixed before relying on it.”
45
45
 
46
+ **Point out:** Session capture, notes, compaction, artifacts, typed memory,
47
+ capability governance, and evaluation remain off for the core-memory proof.
48
+
46
49
  ## 05:00 - Launch and remember
47
50
 
48
51
  **Do:** Start `./.ai/start-claude.sh`.
package/docs/operating.md CHANGED
@@ -16,8 +16,9 @@ installs. This guide covers all three in order.
16
16
 
17
17
  ## Prerequisites
18
18
 
19
- - Node.js 22 or newer (for the memory server) and a supported harness: Claude
20
- Code, OpenCode, or Pi.
19
+ - Node.js 22 or newer (for the memory server) and a supported harness. Claude
20
+ Code and OpenCode receive the full operating layer. Kimi Code receives the
21
+ memory MCP and launcher. Pi receives its native trajectory adapter.
21
22
  - Optional: the `sqlite3` CLI for `cairn memory export`. Runtime memory and
22
23
  `cairn memory import` do not require it.
23
24
  - Optional: an OpenAI-compatible LLM endpoint for memory extraction and
@@ -157,6 +158,37 @@ Model selection stays operator-env-driven through the existing
157
158
  variables (see "Configuration" below) — the harness commits no default model
158
159
  and carries no known-good allowlist.
159
160
 
161
+ ## Setup order (Kimi Code)
162
+
163
+ Kimi Code can use the Cairnkeep memory MCP and the generated project launcher.
164
+ It does not yet receive Cairnkeep-specific skills, commands, or hooks.
165
+
166
+ For local stdio memory, create `.kimi-code/mcp.json` in the project:
167
+
168
+ ```json
169
+ {
170
+ "mcpServers": {
171
+ "cairn-memory": {
172
+ "command": "cairn",
173
+ "args": ["memory-server"]
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ Then launch from the project root with `./.ai/start-kimi.sh`. The launcher
180
+ loads `.ai/.env`, runs the optional pre-launch and post-exit hooks, and forwards
181
+ all arguments to `kimi`.
182
+
183
+ Remote HTTP configuration needs one extra precaution: Kimi validates `url` as
184
+ a literal URL and does not expand `${CAIRN_MEMORY_REMOTE_URL}` there. Put the
185
+ resolved URL in a private `.kimi-code/mcp.json` and use
186
+ `bearerTokenEnvVar: "CAIRN_MEMORY_HTTP_TOKEN"` so the token value remains in
187
+ the environment. A repository-root `.mcp.json` must also be valid because Kimi
188
+ parses it before applying its private override. See
189
+ [Harness compatibility](harness-compatibility.md#kimi-code) for the complete
190
+ configuration and trust-boundary guidance.
191
+
160
192
  ## Setup order (Pi)
161
193
 
162
194
  Pi trajectory capture uses the same project scaffold and store, but its source
@@ -630,13 +662,14 @@ maintain a running install — without forking the core. All are opt-in.
630
662
 
631
663
  ### Launcher seams
632
664
 
633
- The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`) run three
634
- optional hooks around the harness, each a no-op when absent:
665
+ The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`,
666
+ `.ai/start-kimi.sh`, and `.ai/start-pi.sh`) run optional hooks around the
667
+ harness, each a no-op when absent:
635
668
 
636
669
  | Seam | When | Purpose |
637
670
  |---|---|---|
638
671
  | `.ai/pre-launch.sh` | sourced after `.env`, before launch | export env (e.g. a provider base URL / auth), refresh credentials, or abort by returning non-zero |
639
- | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered on the harness (`--settings` / `OPENCODE_CONFIG`); process env still wins over it |
672
+ | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered on Claude Code (`--settings`) or OpenCode (`OPENCODE_CONFIG`); Kimi and Pi leave the variable available to hooks but do not interpret it |
640
673
  | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
641
674
 
642
675
  A wrapper that needs a non-default provider drops a `pre-launch.sh` that renders
@@ -1 +1 @@
1
- {"identifier":"cairn-offline-fake-v1","package_version":"2.4.0","schema_version":1,"task_set_digest":"13e95379f35839593047ec34521a65a8fa3ab92bd3993bca9dc8bf5c4a9ff36f"}
1
+ {"identifier":"cairn-offline-fake-v1","package_version":"2.5.0","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.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "A durable, harness-agnostic memory + context layer for coding agents (Claude Code, OpenCode, Pi).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -56,6 +56,7 @@ install_file() {
56
56
  install_file "$TPL/start-claude.sh.template" "$target/.ai/start-claude.sh" 0755
57
57
  install_file "$TPL/start-opencode.sh.template" "$target/.ai/start-opencode.sh" 0755
58
58
  install_file "$TPL/start-pi.sh.template" "$target/.ai/start-pi.sh" 0755
59
+ install_file "$TPL/start-kimi.sh.template" "$target/.ai/start-kimi.sh" 0755
59
60
  install_file "$TPL/env.example.template" "$target/.ai/env.example" 0644
60
61
  install_file "$TPL/trajectory-redaction.json.template" "$target/.ai/trajectory-redaction.json" 0644
61
62
  install_file "$TPL/capabilities.json.template" "$target/.ai/capabilities.json" 0600
@@ -105,3 +106,4 @@ echo " 3. Install the operating layer (commands/agents/hooks): cairn sync --app
105
106
  echo " For Pi trajectory capture, also run: cairn sync-pi --apply"
106
107
  echo " From a source clone, use bin/cairn in place of cairn."
107
108
  echo " 4. Launch: $target/.ai/start-claude.sh"
109
+ echo " Kimi memory-client launch: $target/.ai/start-kimi.sh"
@@ -53,6 +53,7 @@ mkdir "$tmp/repo"
53
53
  git -C "$tmp/repo" init -q
54
54
  "$ROOT/scripts/bootstrap.sh" --untracked "$tmp/repo" >/dev/null
55
55
  [[ -f "$tmp/repo/.ai/start-claude.sh" ]] || fail "scaffold missing"
56
+ [[ -x "$tmp/repo/.ai/start-kimi.sh" ]] || fail "Kimi launcher missing"
56
57
  [[ -f "$tmp/repo/.planning/config.json" ]] || fail "planning layer missing"
57
58
  [[ -f "$tmp/repo/.agentfs/.gitignore" ]] || fail "project-memory ignore guard missing"
58
59
  [[ -z "$(git -C "$tmp/repo" status --porcelain)" ]] || fail "scaffold visible to git"
@@ -66,7 +67,7 @@ grep -qxF "/.agentfs/" "$tmp/repo/.git/info/exclude" || fail "missing /.agentfs/
66
67
  printf '%s\n' '{"schema_version":1,"capabilities":{"wiki":false},"logging":{"callbacks":true}}' >"$tmp/repo/.ai/capabilities.json"
67
68
  chmod 600 "$tmp/repo/.ai/capabilities.json"
68
69
  printf '%s\n' '# operator-owned environment' >"$tmp/repo/.ai/env.example"
69
- asset_paths='.ai/start-claude.sh .ai/start-opencode.sh .ai/start-pi.sh .ai/env.example .ai/trajectory-redaction.json .ai/capabilities.json .agentfs/.gitignore .planning/config.json .planning/wiki/index.md .planning/wiki/policy.md .planning/wiki/CONTRADICTIONS.md .planning/wiki/LOG.md .planning/alignment/policy.md .planning/alignment/gap-register.yaml .planning/graphs/policy.md .planning/graphs/.gitignore .planning/security/policy.md'
70
+ asset_paths='.ai/start-claude.sh .ai/start-opencode.sh .ai/start-pi.sh .ai/start-kimi.sh .ai/env.example .ai/trajectory-redaction.json .ai/capabilities.json .agentfs/.gitignore .planning/config.json .planning/wiki/index.md .planning/wiki/policy.md .planning/wiki/CONTRADICTIONS.md .planning/wiki/LOG.md .planning/alignment/policy.md .planning/alignment/gap-register.yaml .planning/graphs/policy.md .planning/graphs/.gitignore .planning/security/policy.md'
70
71
  mkdir -p "$tmp/assets-before"
71
72
  for path in $asset_paths; do
72
73
  cp "$tmp/repo/$path" "$tmp/assets-before/${path//\//__}"
@@ -423,6 +423,8 @@ launcher="$repo/.ai/start-claude.sh"
423
423
  [[ -x "$launcher" ]] || fail "launcher not scaffolded"
424
424
  pi_launcher="$repo/.ai/start-pi.sh"
425
425
  [[ -x "$pi_launcher" ]] || fail "Pi launcher not scaffolded"
426
+ kimi_launcher="$repo/.ai/start-kimi.sh"
427
+ [[ -x "$kimi_launcher" ]] || fail "Kimi launcher not scaffolded"
426
428
 
427
429
  # OpenCode's run mode ends a turn after content-only output. Commands that need
428
430
  # owner I/O must therefore make their first response action a tool call rather
@@ -464,11 +466,17 @@ cat > "$tmp/bin/opencode" <<'FAKE'
464
466
  } > "$OPENCODE_LOG"
465
467
  exit "${FAKE_EXIT:-0}"
466
468
  FAKE
467
- chmod +x "$tmp/bin/claude" "$tmp/bin/opencode"
469
+ cat > "$tmp/bin/kimi" <<'FAKE'
470
+ #!/usr/bin/env bash
471
+ { echo "args:$*"; echo "prelaunch:${PRELAUNCH_RAN:-0}"; echo "extra:${CAIRN_EXTRA_SETTINGS:-}"; } > "$KIMI_LOG"
472
+ exit "${FAKE_EXIT:-0}"
473
+ FAKE
474
+ chmod +x "$tmp/bin/claude" "$tmp/bin/opencode" "$tmp/bin/kimi"
468
475
  ln -s "$ROOT/bin/cairn" "$tmp/bin/cairn"
469
476
  export PATH="$tmp/bin:$PATH"
470
477
  export CLAUDE_LOG="$tmp/claude.log"
471
478
  export OPENCODE_LOG="$tmp/opencode.log"
479
+ export KIMI_LOG="$tmp/kimi.log"
472
480
 
473
481
  # Claude sync has three exact installation states. Normal sync and an explicit
474
482
  # overlay request with the master off must contain no capability hook bytes or
@@ -528,6 +536,10 @@ grep -qx "args:--foo bar" "$OPENCODE_LOG" || fail "OpenCode baseline did not pas
528
536
  grep -qx "config:" "$OPENCODE_LOG" || fail "OpenCode baseline selected a config root"
529
537
  grep -qx "capability-registration:0" "$OPENCODE_LOG" || fail "OpenCode baseline registered a capability plugin"
530
538
 
539
+ "$kimi_launcher" --foo bar >/dev/null 2>&1 || fail "Kimi baseline launch failed"
540
+ grep -qx "args:--foo bar" "$KIMI_LOG" || fail "Kimi baseline did not pass args through"
541
+ grep -qx "prelaunch:0" "$KIMI_LOG" || fail "Kimi prelaunch marker leaked without a hook"
542
+
531
543
  # Invalid/off master values stay on the legacy direct-exec path: one harness
532
544
  # process, unchanged argv/config environment, and no isolated assets or store.
533
545
  legacy_claude="$tmp/legacy-claude"
@@ -618,6 +630,12 @@ grep -qx "prelaunch:1" "$OPENCODE_LOG" || fail "OpenCode pre-launch env not visi
618
630
  grep -qx "args:--settings-probe" "$OPENCODE_LOG" || fail "OpenCode extra config changed harness argv"
619
631
  grep -qx "explicit-config:$tmp/settings.json" "$OPENCODE_LOG" || fail "CAIRN_EXTRA_SETTINGS not exported as OPENCODE_CONFIG"
620
632
 
633
+ rm -f "$KIMI_LOG"
634
+ "$kimi_launcher" --plan >/dev/null 2>&1 || fail "Kimi launch with pre-launch failed"
635
+ grep -qx "args:--plan" "$KIMI_LOG" || fail "Kimi pre-launch changed harness argv"
636
+ grep -qx "prelaunch:1" "$KIMI_LOG" || fail "Kimi pre-launch env not visible to harness"
637
+ grep -qx "extra:$tmp/settings.json" "$KIMI_LOG" || fail "Kimi pre-launch environment was not preserved"
638
+
621
639
  # 3. pre-launch.sh non-zero aborts the launch before the harness runs.
622
640
  rm -f "$CLAUDE_LOG"
623
641
  cat > "$repo/.ai/pre-launch.sh" <<'EOF'
@@ -626,6 +644,9 @@ return 1
626
644
  EOF
627
645
  if "$launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort the launch"; fi
628
646
  [[ ! -f "$CLAUDE_LOG" ]] || fail "harness ran despite pre-launch abort"
647
+ rm -f "$KIMI_LOG"
648
+ if "$kimi_launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort Kimi"; fi
649
+ [[ ! -f "$KIMI_LOG" ]] || fail "Kimi ran despite pre-launch abort"
629
650
  rm -f "$repo/.ai/pre-launch.sh"
630
651
 
631
652
  # 4. post-exit.sh runs after the harness with $CAIRN_EXIT_STATUS.
@@ -638,6 +659,13 @@ status=$?
638
659
  [[ "$status" -eq 7 ]] || fail "expected exit 7 from launcher, got $status"
639
660
  grep -qx "post:7" "$tmp/post.log" || fail "post-exit hook did not see CAIRN_EXIT_STATUS"
640
661
 
662
+ rm -f "$tmp/post.log" "$KIMI_LOG"
663
+ FAKE_EXIT=8 "$kimi_launcher" --auto >/dev/null 2>&1 && fail "Kimi launcher should propagate harness exit code"
664
+ status=$?
665
+ [[ "$status" -eq 8 ]] || fail "expected exit 8 from Kimi launcher, got $status"
666
+ grep -qx "args:--auto" "$KIMI_LOG" || fail "Kimi launcher changed arguments"
667
+ grep -qx "post:8" "$tmp/post.log" || fail "Kimi launcher post-exit status missing"
668
+
641
669
  # 5. Pi launcher preserves the same env/pre/post seams and passes arguments
642
670
  # unchanged; Pi has no generic settings-file flag so CAIRN_EXTRA_SETTINGS is ignored.
643
671
  rm -f "$repo/.ai/pre-launch.sh" "$repo/.ai/post-exit.sh"
@@ -7,13 +7,22 @@ cd "$ROOT"
7
7
  version=$(node -p "require('./package.json').version")
8
8
  landing=docs/learning/README.md
9
9
  production=docs/learning/PRODUCTION-PLAN.md
10
+ coverage=docs/learning/CURRICULUM-MAP.md
11
+ feature_guide=docs/learning/FEATURE-GUIDE.md
10
12
 
11
- [[ -f "$landing" && -f "$production" ]]
13
+ [[ -f "$landing" && -f "$production" && -f "$coverage" && -f "$feature_guide" ]]
12
14
  grep -qF 'docs/learning/README.md' README.md
15
+ grep -qF "**Baseline:** Cairnkeep $version" "$coverage"
16
+ grep -qF "**Baseline:** Cairnkeep $version" "$feature_guide"
17
+ grep -qF 'https://github.com/cairnkeep/cairnkeep-course-labs' "$landing"
18
+ for checkpoint in course-00-app course-01-bootstrap course-02-memory course-03-quality \
19
+ course-04-operation course-05-evidence course-06-governance course-07-evaluation; do
20
+ grep -qF "\`$checkpoint\`" "$coverage"
21
+ done
13
22
 
14
23
  ready=0
15
24
  brief=0
16
- for number in $(seq -w 0 12); do
25
+ for number in $(seq -w 0 17); do
17
26
  matches=(docs/learning/lessons/L"$number"-*.md)
18
27
  [[ ${#matches[@]} -eq 1 && -f ${matches[0]} ]] || {
19
28
  echo "learning path must contain exactly one L$number lesson" >&2
@@ -41,17 +50,24 @@ for number in $(seq -w 0 12); do
41
50
  fi
42
51
  done
43
52
 
44
- [[ $ready -eq 4 && $brief -eq 9 ]] || {
53
+ [[ $ready -eq 4 && $brief -eq 14 ]] || {
45
54
  echo "unexpected learning status totals: ready=$ready brief=$brief" >&2
46
55
  exit 1
47
56
  }
48
57
 
49
- for track in quickstart practitioner operator; do
58
+ for track in quickstart practitioner evidence-and-evaluation operator; do
50
59
  file=docs/learning/tracks/$track.md
51
60
  [[ -f "$file" ]]
52
61
  grep -q '^# ' "$file"
53
62
  done
54
63
 
64
+ while read -r command; do
65
+ grep -qF "\`cairn $command\`" "$coverage" || {
66
+ echo "top-level command has no curriculum owner: cairn $command" >&2
67
+ exit 1
68
+ }
69
+ done < <(bin/cairn help | sed -n 's/^ cairn \([a-z-]*\).*/\1/p' | sort -u)
70
+
55
71
  node <<'NODE'
56
72
  const { existsSync, readdirSync, readFileSync } = require("node:fs");
57
73
  const { dirname, resolve } = require("node:path");
@@ -43,6 +43,7 @@ bootstrap_output=$(cairn bootstrap "$tmp/project")
43
43
  [[ "$bootstrap_output" == *"cairn sync --apply"* ]] || fail "bootstrap printed invalid operating-layer setup instructions"
44
44
  [[ -x "$tmp/project/.ai/start-claude.sh" ]] || fail "bootstrap did not install an executable Claude launcher"
45
45
  [[ -x "$tmp/project/.ai/start-pi.sh" ]] || fail "bootstrap did not install an executable Pi launcher"
46
+ [[ -x "$tmp/project/.ai/start-kimi.sh" ]] || fail "bootstrap did not install an executable Kimi launcher"
46
47
  [[ -f "$tmp/project/.planning/config.json" ]] || fail "bootstrap did not install the planning scaffold"
47
48
 
48
49
  (cd "$tmp/project" && cairn doctor) >/dev/null || fail "installed package failed cairn doctor"
@@ -220,6 +221,8 @@ NODE
220
221
  fail "npm tarball omitted the Pi sync command"
221
222
  [[ -f "$installed_root/templates/start-pi.sh.template" ]] || \
222
223
  fail "npm tarball omitted the Pi launcher template"
224
+ [[ -f "$installed_root/templates/start-kimi.sh.template" ]] || \
225
+ fail "npm tarball omitted the Kimi launcher template"
223
226
  [[ -f "$installed_root/schemas/note.schema.json" ]] || \
224
227
  fail "npm tarball omitted the note schema"
225
228
  [[ -f "$installed_root/schemas/memory-node.schema.json" ]] || \
@@ -39,6 +39,7 @@ PROJECT_AI_FILES=(
39
39
  start-claude.sh
40
40
  start-opencode.sh
41
41
  start-pi.sh
42
+ start-kimi.sh
42
43
  env.example
43
44
  trajectory-redaction.json
44
45
  capabilities.json
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # Cairnkeep launcher for Kimi Code. Loads this repo's .ai/.env (if present),
3
+ # then launches Kimi from the repo root. Kimi reads a valid project .mcp.json
4
+ # and an optional .kimi-code/mcp.json override; it does not expand environment
5
+ # references in an HTTP URL, so remote URLs must be materialized by private
6
+ # machine configuration rather than committed to a shared project file.
7
+ #
8
+ # Wrapper seams (both optional, both no-ops when absent):
9
+ # .ai/pre-launch.sh sourced after .env, before launch. May export env
10
+ # or abort the launch by returning/exiting non-zero.
11
+ # .ai/post-exit.sh sourced after Kimi exits, with
12
+ # $CAIRN_EXIT_STATUS set to the exit code.
13
+ set -euo pipefail
14
+ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
15
+
16
+ if [[ -f "$ROOT_DIR/.ai/.env" ]]; then
17
+ set -a
18
+ # shellcheck disable=SC1091
19
+ . "$ROOT_DIR/.ai/.env"
20
+ set +a
21
+ fi
22
+ if [[ -f "$ROOT_DIR/.ai/pre-launch.sh" ]]; then
23
+ # shellcheck disable=SC1091
24
+ . "$ROOT_DIR/.ai/pre-launch.sh"
25
+ fi
26
+
27
+ cd "$ROOT_DIR"
28
+
29
+ if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
30
+ set +e
31
+ kimi "$@"
32
+ CAIRN_EXIT_STATUS=$?
33
+ set -e
34
+ export CAIRN_EXIT_STATUS
35
+ # shellcheck disable=SC1091
36
+ . "$ROOT_DIR/.ai/post-exit.sh" || true
37
+ exit "$CAIRN_EXIT_STATUS"
38
+ fi
39
+ exec kimi "$@"