@cairnkeep/cli 2.4.0 → 2.6.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 (42) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +21 -3
  3. package/docs/building-an-overlay.md +4 -3
  4. package/docs/harness-compatibility.md +195 -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 +31 -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 +70 -5
  33. package/examples/eval/bundled-fake.json +1 -1
  34. package/package.json +3 -2
  35. package/scripts/bootstrap.sh +4 -0
  36. package/scripts/test-bootstrap-untracked.sh +3 -1
  37. package/scripts/test-launcher-hooks.sh +57 -1
  38. package/scripts/test-learning-docs.sh +20 -4
  39. package/scripts/test-package-install.sh +6 -0
  40. package/scripts/uninstall.sh +2 -0
  41. package/templates/start-kimi.sh.template +39 -0
  42. package/templates/start-qwen.sh.template +32 -0
@@ -1,14 +1,20 @@
1
1
  # Operator Track
2
2
 
3
3
  **Audience:** advanced users and platform maintainers
4
- **Prerequisite:** Practitioner track
5
- **Total time:** about 3 hours
4
+ **Prerequisite:** Practitioner track; L13 before operating captured evidence
5
+ **Total time:** about 4 hours
6
6
 
7
7
  1. [L09 - Domain knowledge with RAG](../lessons/L09-domain-knowledge.md)
8
8
  2. [L10 - Faster context exploration](../lessons/L10-context-exploration.md)
9
9
  3. [L11 - Containers and isolation](../lessons/L11-containers.md)
10
10
  4. [L12 - Managed overlays](../lessons/L12-managed-overlays.md)
11
+ 5. [L15 - Capability governance](../lessons/L15-capability-governance.md)
12
+ 6. [L16 - Evaluation and ablation](../lessons/L16-evaluation.md)
11
13
 
12
14
  All modules are optional. The track is complete when the operator can add and
13
15
  remove each integration without breaking the standalone local workflow and can
14
16
  explain its storage, network, credential, and rollback boundaries.
17
+
18
+ Hands-on spine: `course-04-operation`, `course-06-governance`, and
19
+ `course-07-evaluation` in the public
20
+ [course labs](https://github.com/cairnkeep/cairnkeep-course-labs).
@@ -2,14 +2,19 @@
2
2
 
3
3
  **Audience:** developers adopting Cairnkeep in daily work
4
4
  **Prerequisite:** Quickstart track
5
- **Total time:** about 3.5 hours
5
+ **Total time:** about 4 hours
6
6
 
7
7
  1. [L04 - Memory fundamentals](../lessons/L04-memory-fundamentals.md)
8
8
  2. [L05 - The daily workflow](../lessons/L05-daily-workflow.md)
9
9
  3. [L06 - Repository review and security](../lessons/L06-repository-quality.md)
10
10
  4. [L07 - Privacy and storage](../lessons/L07-privacy-and-storage.md)
11
11
  5. [L08 - Multiple machines](../lessons/L08-multiple-machines.md)
12
+ 6. [L13 - Session evidence and hindsight](../lessons/L13-session-evidence.md)
12
13
 
13
14
  The track is complete when the learner can decide what should become memory,
14
15
  review derived knowledge, run governed repository checks, locate the store, and
15
16
  perform a reversible backup or migration.
17
+
18
+ Hands-on spine: `course-02-memory`, `course-03-quality`, and
19
+ `course-05-evidence` in the public
20
+ [course labs](https://github.com/cairnkeep/cairnkeep-course-labs).
@@ -12,3 +12,6 @@
12
12
  The track is complete when a learner can start a new harness session, retrieve
13
13
  a previously accepted fact, and explain that default stdio memory is stored on
14
14
  the local machine rather than a discovered remote service.
15
+
16
+ Hands-on spine: `course-00-app` through `course-01-bootstrap` in the public
17
+ [course labs](https://github.com/cairnkeep/cairnkeep-course-labs).
@@ -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,10 @@ 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 and Qwen Code
21
+ receive the memory MCP and launcher. Pi receives its native trajectory
22
+ adapter.
21
23
  - Optional: the `sqlite3` CLI for `cairn memory export`. Runtime memory and
22
24
  `cairn memory import` do not require it.
23
25
  - Optional: an OpenAI-compatible LLM endpoint for memory extraction and
@@ -157,6 +159,68 @@ Model selection stays operator-env-driven through the existing
157
159
  variables (see "Configuration" below) — the harness commits no default model
158
160
  and carries no known-good allowlist.
159
161
 
162
+ ## Setup order (Kimi Code)
163
+
164
+ Kimi Code can use the Cairnkeep memory MCP and the generated project launcher.
165
+ It does not yet receive Cairnkeep-specific skills, commands, or hooks.
166
+
167
+ For local stdio memory, create `.kimi-code/mcp.json` in the project:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "cairn-memory": {
173
+ "command": "cairn",
174
+ "args": ["memory-server"]
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ Then launch from the project root with `./.ai/start-kimi.sh`. The launcher
181
+ loads `.ai/.env`, runs the optional pre-launch and post-exit hooks, and forwards
182
+ all arguments to `kimi`.
183
+
184
+ Remote HTTP configuration needs one extra precaution: Kimi validates `url` as
185
+ a literal URL and does not expand `${CAIRN_MEMORY_REMOTE_URL}` there. Put the
186
+ resolved URL in a private `.kimi-code/mcp.json` and use
187
+ `bearerTokenEnvVar: "CAIRN_MEMORY_HTTP_TOKEN"` so the token value remains in
188
+ the environment. A repository-root `.mcp.json` must also be valid because Kimi
189
+ parses it before applying its private override. See
190
+ [Harness compatibility](harness-compatibility.md#kimi-code) for the complete
191
+ configuration and trust-boundary guidance.
192
+
193
+ ## Setup order (Qwen Code)
194
+
195
+ Qwen Code can use the Cairnkeep memory MCP and the generated project launcher.
196
+ It does not yet receive Cairnkeep-specific skills, commands, or hooks.
197
+
198
+ For local stdio memory, create `.qwen/settings.json` in the project:
199
+
200
+ ```json
201
+ {
202
+ "mcpServers": {
203
+ "cairn-memory": {
204
+ "command": "cairn",
205
+ "args": ["memory-server"]
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ Review and approve the project server, then launch it:
212
+
213
+ ```bash
214
+ qwen mcp approve cairn-memory
215
+ ./.ai/start-qwen.sh
216
+ ```
217
+
218
+ For authenticated remote HTTP, use `httpUrl` plus environment-expanded headers
219
+ in `.qwen/settings.json`. Keep the endpoint and bearer token in `.ai/.env` or a
220
+ machine secret store, not in the JSON file. See
221
+ [Harness compatibility](harness-compatibility.md#qwen-code) for the tested
222
+ configuration, approval behavior, and overlay guidance.
223
+
160
224
  ## Setup order (Pi)
161
225
 
162
226
  Pi trajectory capture uses the same project scaffold and store, but its source
@@ -630,13 +694,14 @@ maintain a running install — without forking the core. All are opt-in.
630
694
 
631
695
  ### Launcher seams
632
696
 
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:
697
+ The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`,
698
+ `.ai/start-kimi.sh`, `.ai/start-qwen.sh`, and `.ai/start-pi.sh`) run optional hooks around the
699
+ harness, each a no-op when absent:
635
700
 
636
701
  | Seam | When | Purpose |
637
702
  |---|---|---|
638
703
  | `.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 |
704
+ | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered on Claude Code (`--settings`) or OpenCode (`OPENCODE_CONFIG`); Kimi, Qwen, and Pi leave the variable available to hooks but do not interpret it |
640
705
  | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
641
706
 
642
707
  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.6.0","schema_version":1,"task_set_digest":"13e95379f35839593047ec34521a65a8fa3ab92bd3993bca9dc8bf5c4a9ff36f"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cairnkeep/cli",
3
- "version": "2.4.0",
4
- "description": "A durable, harness-agnostic memory + context layer for coding agents (Claude Code, OpenCode, Pi).",
3
+ "version": "2.6.0",
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",
7
7
  "publishConfig": {
@@ -63,6 +63,7 @@
63
63
  "agent",
64
64
  "claude-code",
65
65
  "opencode",
66
+ "qwen-code",
66
67
  "pi-coding-agent",
67
68
  "context",
68
69
  "rag"
@@ -56,6 +56,8 @@ 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
60
+ install_file "$TPL/start-qwen.sh.template" "$target/.ai/start-qwen.sh" 0755
59
61
  install_file "$TPL/env.example.template" "$target/.ai/env.example" 0644
60
62
  install_file "$TPL/trajectory-redaction.json.template" "$target/.ai/trajectory-redaction.json" 0644
61
63
  install_file "$TPL/capabilities.json.template" "$target/.ai/capabilities.json" 0600
@@ -105,3 +107,5 @@ echo " 3. Install the operating layer (commands/agents/hooks): cairn sync --app
105
107
  echo " For Pi trajectory capture, also run: cairn sync-pi --apply"
106
108
  echo " From a source clone, use bin/cairn in place of cairn."
107
109
  echo " 4. Launch: $target/.ai/start-claude.sh"
110
+ echo " Kimi memory-client launch: $target/.ai/start-kimi.sh"
111
+ echo " Qwen memory-client launch: $target/.ai/start-qwen.sh"
@@ -53,6 +53,8 @@ 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"
57
+ [[ -x "$tmp/repo/.ai/start-qwen.sh" ]] || fail "Qwen launcher missing"
56
58
  [[ -f "$tmp/repo/.planning/config.json" ]] || fail "planning layer missing"
57
59
  [[ -f "$tmp/repo/.agentfs/.gitignore" ]] || fail "project-memory ignore guard missing"
58
60
  [[ -z "$(git -C "$tmp/repo" status --porcelain)" ]] || fail "scaffold visible to git"
@@ -66,7 +68,7 @@ grep -qxF "/.agentfs/" "$tmp/repo/.git/info/exclude" || fail "missing /.agentfs/
66
68
  printf '%s\n' '{"schema_version":1,"capabilities":{"wiki":false},"logging":{"callbacks":true}}' >"$tmp/repo/.ai/capabilities.json"
67
69
  chmod 600 "$tmp/repo/.ai/capabilities.json"
68
70
  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'
71
+ asset_paths='.ai/start-claude.sh .ai/start-opencode.sh .ai/start-pi.sh .ai/start-kimi.sh .ai/start-qwen.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
72
  mkdir -p "$tmp/assets-before"
71
73
  for path in $asset_paths; do
72
74
  cp "$tmp/repo/$path" "$tmp/assets-before/${path//\//__}"
@@ -423,6 +423,10 @@ 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"
428
+ qwen_launcher="$repo/.ai/start-qwen.sh"
429
+ [[ -x "$qwen_launcher" ]] || fail "Qwen launcher not scaffolded"
426
430
 
427
431
  # OpenCode's run mode ends a turn after content-only output. Commands that need
428
432
  # owner I/O must therefore make their first response action a tool call rather
@@ -464,11 +468,23 @@ cat > "$tmp/bin/opencode" <<'FAKE'
464
468
  } > "$OPENCODE_LOG"
465
469
  exit "${FAKE_EXIT:-0}"
466
470
  FAKE
467
- chmod +x "$tmp/bin/claude" "$tmp/bin/opencode"
471
+ cat > "$tmp/bin/kimi" <<'FAKE'
472
+ #!/usr/bin/env bash
473
+ { echo "args:$*"; echo "prelaunch:${PRELAUNCH_RAN:-0}"; echo "extra:${CAIRN_EXTRA_SETTINGS:-}"; } > "$KIMI_LOG"
474
+ exit "${FAKE_EXIT:-0}"
475
+ FAKE
476
+ cat > "$tmp/bin/qwen" <<'FAKE'
477
+ #!/usr/bin/env bash
478
+ { echo "args:$*"; echo "prelaunch:${PRELAUNCH_RAN:-0}"; echo "extra:${CAIRN_EXTRA_SETTINGS:-}"; } > "$QWEN_LOG"
479
+ exit "${FAKE_EXIT:-0}"
480
+ FAKE
481
+ chmod +x "$tmp/bin/claude" "$tmp/bin/opencode" "$tmp/bin/kimi" "$tmp/bin/qwen"
468
482
  ln -s "$ROOT/bin/cairn" "$tmp/bin/cairn"
469
483
  export PATH="$tmp/bin:$PATH"
470
484
  export CLAUDE_LOG="$tmp/claude.log"
471
485
  export OPENCODE_LOG="$tmp/opencode.log"
486
+ export KIMI_LOG="$tmp/kimi.log"
487
+ export QWEN_LOG="$tmp/qwen.log"
472
488
 
473
489
  # Claude sync has three exact installation states. Normal sync and an explicit
474
490
  # overlay request with the master off must contain no capability hook bytes or
@@ -528,6 +544,14 @@ grep -qx "args:--foo bar" "$OPENCODE_LOG" || fail "OpenCode baseline did not pas
528
544
  grep -qx "config:" "$OPENCODE_LOG" || fail "OpenCode baseline selected a config root"
529
545
  grep -qx "capability-registration:0" "$OPENCODE_LOG" || fail "OpenCode baseline registered a capability plugin"
530
546
 
547
+ "$kimi_launcher" --foo bar >/dev/null 2>&1 || fail "Kimi baseline launch failed"
548
+ grep -qx "args:--foo bar" "$KIMI_LOG" || fail "Kimi baseline did not pass args through"
549
+ grep -qx "prelaunch:0" "$KIMI_LOG" || fail "Kimi prelaunch marker leaked without a hook"
550
+
551
+ "$qwen_launcher" --foo bar >/dev/null 2>&1 || fail "Qwen baseline launch failed"
552
+ grep -qx "args:--foo bar" "$QWEN_LOG" || fail "Qwen baseline did not pass args through"
553
+ grep -qx "prelaunch:0" "$QWEN_LOG" || fail "Qwen prelaunch marker leaked without a hook"
554
+
531
555
  # Invalid/off master values stay on the legacy direct-exec path: one harness
532
556
  # process, unchanged argv/config environment, and no isolated assets or store.
533
557
  legacy_claude="$tmp/legacy-claude"
@@ -618,6 +642,18 @@ grep -qx "prelaunch:1" "$OPENCODE_LOG" || fail "OpenCode pre-launch env not visi
618
642
  grep -qx "args:--settings-probe" "$OPENCODE_LOG" || fail "OpenCode extra config changed harness argv"
619
643
  grep -qx "explicit-config:$tmp/settings.json" "$OPENCODE_LOG" || fail "CAIRN_EXTRA_SETTINGS not exported as OPENCODE_CONFIG"
620
644
 
645
+ rm -f "$KIMI_LOG"
646
+ "$kimi_launcher" --plan >/dev/null 2>&1 || fail "Kimi launch with pre-launch failed"
647
+ grep -qx "args:--plan" "$KIMI_LOG" || fail "Kimi pre-launch changed harness argv"
648
+ grep -qx "prelaunch:1" "$KIMI_LOG" || fail "Kimi pre-launch env not visible to harness"
649
+ grep -qx "extra:$tmp/settings.json" "$KIMI_LOG" || fail "Kimi pre-launch environment was not preserved"
650
+
651
+ rm -f "$QWEN_LOG"
652
+ "$qwen_launcher" --approval-mode plan >/dev/null 2>&1 || fail "Qwen launch with pre-launch failed"
653
+ grep -qx "args:--approval-mode plan" "$QWEN_LOG" || fail "Qwen pre-launch changed harness argv"
654
+ grep -qx "prelaunch:1" "$QWEN_LOG" || fail "Qwen pre-launch env not visible to harness"
655
+ grep -qx "extra:$tmp/settings.json" "$QWEN_LOG" || fail "Qwen pre-launch environment was not preserved"
656
+
621
657
  # 3. pre-launch.sh non-zero aborts the launch before the harness runs.
622
658
  rm -f "$CLAUDE_LOG"
623
659
  cat > "$repo/.ai/pre-launch.sh" <<'EOF'
@@ -626,6 +662,12 @@ return 1
626
662
  EOF
627
663
  if "$launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort the launch"; fi
628
664
  [[ ! -f "$CLAUDE_LOG" ]] || fail "harness ran despite pre-launch abort"
665
+ rm -f "$KIMI_LOG"
666
+ if "$kimi_launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort Kimi"; fi
667
+ [[ ! -f "$KIMI_LOG" ]] || fail "Kimi ran despite pre-launch abort"
668
+ rm -f "$QWEN_LOG"
669
+ if "$qwen_launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort Qwen"; fi
670
+ [[ ! -f "$QWEN_LOG" ]] || fail "Qwen ran despite pre-launch abort"
629
671
  rm -f "$repo/.ai/pre-launch.sh"
630
672
 
631
673
  # 4. post-exit.sh runs after the harness with $CAIRN_EXIT_STATUS.
@@ -638,6 +680,20 @@ status=$?
638
680
  [[ "$status" -eq 7 ]] || fail "expected exit 7 from launcher, got $status"
639
681
  grep -qx "post:7" "$tmp/post.log" || fail "post-exit hook did not see CAIRN_EXIT_STATUS"
640
682
 
683
+ rm -f "$tmp/post.log" "$KIMI_LOG"
684
+ FAKE_EXIT=8 "$kimi_launcher" --auto >/dev/null 2>&1 && fail "Kimi launcher should propagate harness exit code"
685
+ status=$?
686
+ [[ "$status" -eq 8 ]] || fail "expected exit 8 from Kimi launcher, got $status"
687
+ grep -qx "args:--auto" "$KIMI_LOG" || fail "Kimi launcher changed arguments"
688
+ grep -qx "post:8" "$tmp/post.log" || fail "Kimi launcher post-exit status missing"
689
+
690
+ rm -f "$tmp/post.log" "$QWEN_LOG"
691
+ FAKE_EXIT=10 "$qwen_launcher" --output-format json >/dev/null 2>&1 && fail "Qwen launcher should propagate harness exit code"
692
+ status=$?
693
+ [[ "$status" -eq 10 ]] || fail "expected exit 10 from Qwen launcher, got $status"
694
+ grep -qx "args:--output-format json" "$QWEN_LOG" || fail "Qwen launcher changed arguments"
695
+ grep -qx "post:10" "$tmp/post.log" || fail "Qwen launcher post-exit status missing"
696
+
641
697
  # 5. Pi launcher preserves the same env/pre/post seams and passes arguments
642
698
  # unchanged; Pi has no generic settings-file flag so CAIRN_EXTRA_SETTINGS is ignored.
643
699
  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,8 @@ 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"
47
+ [[ -x "$tmp/project/.ai/start-qwen.sh" ]] || fail "bootstrap did not install an executable Qwen launcher"
46
48
  [[ -f "$tmp/project/.planning/config.json" ]] || fail "bootstrap did not install the planning scaffold"
47
49
 
48
50
  (cd "$tmp/project" && cairn doctor) >/dev/null || fail "installed package failed cairn doctor"
@@ -220,6 +222,10 @@ NODE
220
222
  fail "npm tarball omitted the Pi sync command"
221
223
  [[ -f "$installed_root/templates/start-pi.sh.template" ]] || \
222
224
  fail "npm tarball omitted the Pi launcher template"
225
+ [[ -f "$installed_root/templates/start-kimi.sh.template" ]] || \
226
+ fail "npm tarball omitted the Kimi launcher template"
227
+ [[ -f "$installed_root/templates/start-qwen.sh.template" ]] || \
228
+ fail "npm tarball omitted the Qwen launcher template"
223
229
  [[ -f "$installed_root/schemas/note.schema.json" ]] || \
224
230
  fail "npm tarball omitted the note schema"
225
231
  [[ -f "$installed_root/schemas/memory-node.schema.json" ]] || \
@@ -39,6 +39,8 @@ PROJECT_AI_FILES=(
39
39
  start-claude.sh
40
40
  start-opencode.sh
41
41
  start-pi.sh
42
+ start-kimi.sh
43
+ start-qwen.sh
42
44
  env.example
43
45
  trajectory-redaction.json
44
46
  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 "$@"
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # Cairnkeep launcher for Qwen Code. Loads this repo's private .ai/.env, runs
3
+ # the optional pre/post seams, and launches Qwen from the project root. Qwen
4
+ # reads user ~/.qwen/settings.json plus project .qwen/settings.json and
5
+ # .mcp.json configuration.
6
+ set -euo pipefail
7
+ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
8
+
9
+ if [[ -f "$ROOT_DIR/.ai/.env" ]]; then
10
+ set -a
11
+ # shellcheck disable=SC1091
12
+ . "$ROOT_DIR/.ai/.env"
13
+ set +a
14
+ fi
15
+ if [[ -f "$ROOT_DIR/.ai/pre-launch.sh" ]]; then
16
+ # shellcheck disable=SC1091
17
+ . "$ROOT_DIR/.ai/pre-launch.sh"
18
+ fi
19
+
20
+ cd "$ROOT_DIR"
21
+
22
+ if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
23
+ set +e
24
+ qwen "$@"
25
+ CAIRN_EXIT_STATUS=$?
26
+ set -e
27
+ export CAIRN_EXIT_STATUS
28
+ # shellcheck disable=SC1091
29
+ . "$ROOT_DIR/.ai/post-exit.sh" || true
30
+ exit "$CAIRN_EXIT_STATUS"
31
+ fi
32
+ exec qwen "$@"