@cairnkeep/cli 2.5.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.
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.6.0] - 2026-07-29
9
+
10
+ ### Added
11
+
12
+ - Add Qwen Code as a supported memory client with a generated project launcher,
13
+ local stdio guidance, and tested authenticated remote HTTP configuration.
14
+ - Expand the harness comparison to cover current native plugin and MCP clients,
15
+ while keeping memory-only support separate from the full operating layer.
16
+
8
17
  ## [2.5.0] - 2026-07-29
9
18
 
10
19
  ### Added
@@ -211,7 +220,8 @@ stored memories.
211
220
  - Preserve executable permissions and Bash 3.2 portability on macOS.
212
221
  - Add backup-first uninstall and SQLite-safe memory export/import guidance.
213
222
 
214
- [Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.5.0...HEAD
223
+ [Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.6.0...HEAD
224
+ [2.6.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.5.0...v2.6.0
215
225
  [2.5.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.4.0...v2.5.0
216
226
  [2.4.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.3.1...v2.4.0
217
227
  [2.3.1]: https://github.com/cairnkeep/cairnkeep/compare/v2.3.0...v2.3.1
package/README.md CHANGED
@@ -10,7 +10,7 @@ A *cairn* is a stack of stones left as a trail marker for whoever follows; a
10
10
  *keep* is where you store what matters. **Cairnkeep** is where coding agents
11
11
  stack durable memory — decisions, pitfalls, patterns — and follow the trail
12
12
  across sessions, projects, and harnesses (Claude Code, OpenCode, Kimi Code,
13
- Pi, and other MCP clients).
13
+ Qwen Code, Pi, and other MCP clients).
14
14
 
15
15
  ## Status
16
16
 
@@ -38,6 +38,7 @@ and 20 are end-of-life upstream.
38
38
  | Claude Code on Linux/macOS | Memory server plus commands, agents, hooks, and launchers |
39
39
  | OpenCode on Linux/macOS | Memory server plus commands, plugins, hooks, and launchers |
40
40
  | Kimi Code on Linux/macOS | Memory MCP server plus project launcher; no Cairnkeep operating-layer assets yet |
41
+ | Qwen Code on Linux/macOS | Memory MCP server plus project launcher; no Cairnkeep operating-layer assets yet |
41
42
  | Pi on Linux/macOS | Native opt-in trajectory extension and launcher; no bundled MCP bridge |
42
43
  | Codex CLI | Memory MCP server; no Cairnkeep operating-layer assets |
43
44
  | Other MCP clients | Memory and optional domain-knowledge MCP tools |
@@ -47,7 +48,7 @@ Linux, macOS, Bash 3.2 portability, and clean Node 22/24/26 runtime checks
47
48
  are exercised in CI.
48
49
 
49
50
  See [Harness compatibility](docs/harness-compatibility.md) for exact support
50
- levels, Kimi configuration, and candidates that are not yet runtime-tested.
51
+ levels, Kimi and Qwen configuration, and candidates that are not yet runtime-tested.
51
52
 
52
53
  ## Try with Podman
53
54
 
@@ -159,6 +160,12 @@ configuration requires a literal URL and supports a token reference through
159
160
  `bearerTokenEnvVar`; it does not expand `${VAR}` in the URL. See
160
161
  [Harness compatibility](docs/harness-compatibility.md#kimi-code).
161
162
 
163
+ For Qwen Code, use project `.qwen/settings.json`: local stdio launches
164
+ `cairn memory-server`, while remote HTTP uses `httpUrl` and environment-expanded
165
+ headers. Review and approve project MCP configuration with
166
+ `qwen mcp approve cairn-memory`, then launch with `./.ai/start-qwen.sh`. See
167
+ [Harness compatibility](docs/harness-compatibility.md#qwen-code).
168
+
162
169
  For Pi, install the local trajectory adapter and use the scaffolded launcher:
163
170
 
164
171
  ```bash
@@ -208,8 +215,8 @@ unless a wrapper opts in:
208
215
  export env (e.g. a provider base URL) or abort the launch by returning
209
216
  non-zero. This is where credential refresh / connectivity setup lives.
210
217
  - **`CAIRN_EXTRA_SETTINGS`** — path to a settings file layered onto the harness
211
- (`--settings` for Claude Code, `OPENCODE_CONFIG` for OpenCode). Pi has no equivalent
212
- generic settings-file flag, so its launcher leaves this variable unused.
218
+ (`--settings` for Claude Code, `OPENCODE_CONFIG` for OpenCode). Kimi, Qwen,
219
+ and Pi do not interpret this variable in their generic launchers.
213
220
  Process env still wins, so an inline value beats the profile.
214
221
  - **`.ai/post-exit.sh`** — sourced after the harness exits, with
215
222
  `CAIRN_EXIT_STATUS` set to its exit code.
@@ -28,13 +28,13 @@ manifest, profile-lock, private-registry, and rollback contract.
28
28
  ## The launcher seams
29
29
 
30
30
  The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`,
31
- `.ai/start-kimi.sh`, and `.ai/start-pi.sh`, scaffolded by `cairn bootstrap`) run
31
+ `.ai/start-kimi.sh`, `.ai/start-qwen.sh`, and `.ai/start-pi.sh`, scaffolded by `cairn bootstrap`) run
32
32
  optional hooks, each a no-op when absent:
33
33
 
34
34
  | Seam | When | Use it for |
35
35
  |---|---|---|
36
36
  | `.ai/pre-launch.sh` | sourced after `.env`, before launch | export a provider base URL / credentials, refresh a token, run a connectivity check, or **abort** the launch by returning non-zero |
37
- | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered onto Claude Code (`--settings`) or OpenCode (`OPENCODE_CONFIG`); Kimi and Pi leave it available to hooks but do not interpret it |
37
+ | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered onto Claude Code (`--settings`) or OpenCode (`OPENCODE_CONFIG`); Kimi, Qwen, and Pi leave it available to hooks but do not interpret it |
38
38
  | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
39
39
 
40
40
  **Typical overlay launcher** — a corporate wrapper needs a non-default provider
@@ -11,14 +11,15 @@ commands, hooks, plugins, or trajectory adapters.
11
11
  | Claude Code | Memory MCP, commands, agents, hooks, launcher | Exercised by Cairnkeep tests |
12
12
  | OpenCode | Memory MCP, commands, workflows, plugins, launcher | Exercised by Cairnkeep tests |
13
13
  | Kimi Code | Memory MCP, `AGENTS.md`, launcher | Launcher tested; remote MCP tested with Kimi Code 0.30.0 |
14
+ | Qwen Code | Memory MCP, launcher | Launcher tested; stdio and remote MCP tested with Qwen Code 0.21.1 |
14
15
  | Pi | Native opt-in trajectory extension and launcher | Exercised by Cairnkeep tests; no bundled MCP bridge |
15
16
  | Codex CLI | Memory MCP | Configuration supported; no Cairnkeep operating-layer assets or launcher |
16
17
  | Other MCP clients | Memory and optional domain-knowledge tools | Protocol-compatible; not automatically runtime-tested |
17
18
 
18
19
  Only Claude Code and OpenCode currently receive the complete operating layer.
19
- Kimi skills and hooks require harness-specific adaptation and validation before
20
- they can be presented as equivalent. In Kimi, call MCP tools directly or use
21
- ordinary prompts until that work is complete.
20
+ Kimi and Qwen skills and hooks require harness-specific adaptation and
21
+ validation before they can be presented as equivalent. In either client, call
22
+ MCP tools directly or use ordinary prompts until that work is complete.
22
23
 
23
24
  ## Kimi Code
24
25
 
@@ -91,6 +92,72 @@ token value in either file.
91
92
  Kimi MCP configuration is documented at
92
93
  [Kimi Code MCP](https://www.kimi.com/code/docs/en/kimi-code-cli/customization/mcp.html).
93
94
 
95
+ ## Qwen Code
96
+
97
+ Qwen Code can read MCP definitions from user settings, project
98
+ `.qwen/settings.json`, and compatible repository `.mcp.json` files. Use the
99
+ Qwen-specific project settings file for remote HTTP so its `httpUrl` transport
100
+ and environment expansion are explicit.
101
+
102
+ ### Local stdio
103
+
104
+ Create `.qwen/settings.json` in a trusted project:
105
+
106
+ ```json
107
+ {
108
+ "mcpServers": {
109
+ "cairn-memory": {
110
+ "command": "cairn",
111
+ "args": ["memory-server"]
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ Project MCP definitions are approval-gated. Review the file, then run:
118
+
119
+ ```bash
120
+ qwen mcp approve cairn-memory
121
+ ./.ai/start-qwen.sh
122
+ ```
123
+
124
+ The server is a local child process. Its storage stays on this computer unless
125
+ the operator deliberately replaces this stdio definition with a remote one.
126
+
127
+ ### Remote HTTP
128
+
129
+ Qwen Code 0.21.1 was exercised with environment expansion in project settings:
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "cairn-memory": {
135
+ "httpUrl": "${CAIRN_MEMORY_REMOTE_URL}",
136
+ "headers": {
137
+ "Authorization": "Bearer ${CAIRN_MEMORY_HTTP_TOKEN}",
138
+ "X-Cairn-Project": "example-project",
139
+ "X-Cairn-Scopes": "identity,personal,project",
140
+ "X-Cairn-AnythingLLM-Workspaces": "example-workspace"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ Keep the endpoint and token in `.ai/.env` or a machine secret store. The
148
+ generated launcher exports `.ai/.env` before Qwen reads the settings. Re-run
149
+ `qwen mcp approve cairn-memory` after changing the project definition because
150
+ the approval is bound to the configuration digest.
151
+
152
+ The authenticated recipe above is validated for `.qwen/settings.json`.
153
+ Cairnkeep does not assume that a one-shot `--mcp-config` input performs the same
154
+ environment expansion. A private overlay should write the project settings
155
+ backup-first, preserve unrelated keys, exclude private routing metadata from
156
+ Git, and leave secret values as environment references.
157
+
158
+ Qwen MCP configuration is documented at
159
+ [Qwen Code MCP](https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/).
160
+
94
161
  ## Candidate memory clients
95
162
 
96
163
  The following clients have documented MCP support and are reasonable adapter
@@ -99,17 +166,28 @@ writers, operating assets, or runtime CI:
99
166
 
100
167
  | Client | Documented MCP surface | Cairnkeep status |
101
168
  |---|---|---|
102
- | Gemini CLI | `mcpServers` in `settings.json` | Candidate for stdio and HTTP memory tests |
169
+ | Antigravity CLI | Workspace `.agents/mcp_config.json` and plugins | Current successor candidate for memory plus native operating assets |
170
+ | Cursor | Project `.cursor/mcp.json`, plugins, and Agent CLI | Candidate for memory plus native operating assets |
103
171
  | GitHub Copilot CLI | `copilot mcp` and `.mcp.json` | Candidate for memory plus shared skills |
104
- | Qwen Code | `qwen mcp` and MCP configuration | Candidate for stdio and HTTP memory tests |
172
+ | Factory Droid | Native plugin marketplace | Candidate for reusing a compatible operating-layer package |
105
173
  | goose | MCP extensions and CLI session extension loading | Candidate for memory and recipe integration |
174
+ | Gemini CLI | `mcpServers` in `settings.json` | Legacy candidate for enterprise or API-key users only |
106
175
 
107
176
  Primary references:
108
177
 
109
- - [Gemini CLI MCP](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md)
178
+ - [Antigravity MCP](https://antigravity.google/docs/mcp)
179
+ - [Cursor MCP](https://docs.cursor.com/context/model-context-protocol)
110
180
  - [GitHub Copilot CLI MCP](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers)
111
- - [Qwen Code MCP](https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/)
112
181
  - [goose](https://github.com/aaif-goose/goose)
182
+ - [Gemini CLI transition announcement](https://github.com/google-gemini/gemini-cli/discussions/28017)
183
+ - [Superpowers harness integrations](https://github.com/obra/superpowers)
184
+
185
+ Superpowers is a useful comparison because it keeps shared behavior in one
186
+ source tree and ships thin native adapters through each harness's supported
187
+ plugin, extension, hook, or context mechanism. Cairnkeep follows the same
188
+ principle for future operating-layer ports: reuse common content, map real tool
189
+ names and lifecycle events per harness, and document degraded capabilities
190
+ instead of copying one harness's files into another.
113
191
 
114
192
  Support should be added one client at a time with a clean install, a local
115
193
  stdio handshake, an authenticated HTTP handshake, a real write/recall canary,
@@ -1,6 +1,6 @@
1
1
  # Curriculum Coverage Map
2
2
 
3
- **Baseline:** Cairnkeep 2.5.0
3
+ **Baseline:** Cairnkeep 2.6.0
4
4
  **Last reviewed:** 2026-07-29
5
5
 
6
6
  This map prevents the course from drifting behind the product. It assigns each
@@ -1,6 +1,6 @@
1
1
  # Cairnkeep feature guide
2
2
 
3
- **Baseline:** Cairnkeep 2.5.0
3
+ **Baseline:** Cairnkeep 2.6.0
4
4
 
5
5
  This is the step-back map for choosing and operating Cairnkeep features. The
6
6
  [course labs](https://github.com/cairnkeep/cairnkeep-course-labs) provide one
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 10 minutes
6
- **Tested with:** Cairnkeep 2.5.0
6
+ **Tested with:** Cairnkeep 2.6.0
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 15 minutes
6
- **Tested with:** Cairnkeep 2.5.0
6
+ **Tested with:** Cairnkeep 2.6.0
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.5.0
29
+ podman pull ghcr.io/cairnkeep/cairnkeep:2.6.0
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.5.0 stdio
41
+ ghcr.io/cairnkeep/cairnkeep:2.6.0 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.5.0 and Node.js 22 or newer
6
+ **Tested with:** Cairnkeep 2.6.0 and Node.js 22 or newer
7
7
 
8
8
  ## Outcome
9
9
 
@@ -13,7 +13,7 @@ layer, and distinguish a complete installation from a partial one.
13
13
  ## Prerequisites
14
14
 
15
15
  - Node.js 22 or newer and npm.
16
- - Claude Code installed for the commands below. OpenCode and Kimi Code users
16
+ - Claude Code installed for the commands below. OpenCode, Kimi Code, and Qwen Code users
17
17
  should follow the equivalent setup in
18
18
  [the operating guide](../../operating.md).
19
19
  - Permission to install an npm package globally for your user.
@@ -41,7 +41,7 @@ machine-level steps.
41
41
  2. Install Cairnkeep:
42
42
 
43
43
  ```bash
44
- npm install --global @cairnkeep/cli@2.5.0
44
+ npm install --global @cairnkeep/cli@2.6.0
45
45
  cairn --version
46
46
  ```
47
47
 
@@ -108,6 +108,14 @@ URL rather than `${CAIRN_MEMORY_REMOTE_URL}`. Keep the resolved URL in a private
108
108
  `.kimi-code/mcp.json` and keep the bearer token in the environment. See
109
109
  [Harness compatibility](../../harness-compatibility.md#kimi-code).
110
110
 
111
+ Qwen Code can use the generated `.ai/start-qwen.sh` launcher. Its project
112
+ `.qwen/settings.json` supports environment references for both the remote URL
113
+ and authorization header, so the file can contain names such as
114
+ `${CAIRN_MEMORY_REMOTE_URL}` and `${CAIRN_MEMORY_HTTP_TOKEN}` instead of secret
115
+ values. Keep this project settings file untracked and approve the generated MCP
116
+ entry with `qwen mcp approve cairn-memory`. See
117
+ [Harness compatibility](../../harness-compatibility.md#qwen-code).
118
+
111
119
  ## Clean up if you stop the evaluation
112
120
 
113
121
  Continue directly to L03 if you are completing the quickstart. If you stop here,
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 25 minutes
6
- **Tested with:** Cairnkeep 2.5.0
6
+ **Tested with:** Cairnkeep 2.6.0
7
7
 
8
8
  ## Outcome
9
9
 
package/docs/operating.md CHANGED
@@ -17,8 +17,9 @@ installs. This guide covers all three in order.
17
17
  ## Prerequisites
18
18
 
19
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.
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.
22
23
  - Optional: the `sqlite3` CLI for `cairn memory export`. Runtime memory and
23
24
  `cairn memory import` do not require it.
24
25
  - Optional: an OpenAI-compatible LLM endpoint for memory extraction and
@@ -189,6 +190,37 @@ parses it before applying its private override. See
189
190
  [Harness compatibility](harness-compatibility.md#kimi-code) for the complete
190
191
  configuration and trust-boundary guidance.
191
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
+
192
224
  ## Setup order (Pi)
193
225
 
194
226
  Pi trajectory capture uses the same project scaffold and store, but its source
@@ -663,13 +695,13 @@ maintain a running install — without forking the core. All are opt-in.
663
695
  ### Launcher seams
664
696
 
665
697
  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
698
+ `.ai/start-kimi.sh`, `.ai/start-qwen.sh`, and `.ai/start-pi.sh`) run optional hooks around the
667
699
  harness, each a no-op when absent:
668
700
 
669
701
  | Seam | When | Purpose |
670
702
  |---|---|---|
671
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 |
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 |
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 |
673
705
  | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
674
706
 
675
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.5.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.5.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"
@@ -57,6 +57,7 @@ install_file "$TPL/start-claude.sh.template" "$target/.ai/start-claude.sh" 0
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
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
60
61
  install_file "$TPL/env.example.template" "$target/.ai/env.example" 0644
61
62
  install_file "$TPL/trajectory-redaction.json.template" "$target/.ai/trajectory-redaction.json" 0644
62
63
  install_file "$TPL/capabilities.json.template" "$target/.ai/capabilities.json" 0600
@@ -107,3 +108,4 @@ echo " For Pi trajectory capture, also run: cairn sync-pi --apply"
107
108
  echo " From a source clone, use bin/cairn in place of cairn."
108
109
  echo " 4. Launch: $target/.ai/start-claude.sh"
109
110
  echo " Kimi memory-client launch: $target/.ai/start-kimi.sh"
111
+ echo " Qwen memory-client launch: $target/.ai/start-qwen.sh"
@@ -54,6 +54,7 @@ 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
56
  [[ -x "$tmp/repo/.ai/start-kimi.sh" ]] || fail "Kimi launcher missing"
57
+ [[ -x "$tmp/repo/.ai/start-qwen.sh" ]] || fail "Qwen launcher missing"
57
58
  [[ -f "$tmp/repo/.planning/config.json" ]] || fail "planning layer missing"
58
59
  [[ -f "$tmp/repo/.agentfs/.gitignore" ]] || fail "project-memory ignore guard missing"
59
60
  [[ -z "$(git -C "$tmp/repo" status --porcelain)" ]] || fail "scaffold visible to git"
@@ -67,7 +68,7 @@ grep -qxF "/.agentfs/" "$tmp/repo/.git/info/exclude" || fail "missing /.agentfs/
67
68
  printf '%s\n' '{"schema_version":1,"capabilities":{"wiki":false},"logging":{"callbacks":true}}' >"$tmp/repo/.ai/capabilities.json"
68
69
  chmod 600 "$tmp/repo/.ai/capabilities.json"
69
70
  printf '%s\n' '# operator-owned environment' >"$tmp/repo/.ai/env.example"
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'
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'
71
72
  mkdir -p "$tmp/assets-before"
72
73
  for path in $asset_paths; do
73
74
  cp "$tmp/repo/$path" "$tmp/assets-before/${path//\//__}"
@@ -425,6 +425,8 @@ pi_launcher="$repo/.ai/start-pi.sh"
425
425
  [[ -x "$pi_launcher" ]] || fail "Pi launcher not scaffolded"
426
426
  kimi_launcher="$repo/.ai/start-kimi.sh"
427
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"
428
430
 
429
431
  # OpenCode's run mode ends a turn after content-only output. Commands that need
430
432
  # owner I/O must therefore make their first response action a tool call rather
@@ -471,12 +473,18 @@ cat > "$tmp/bin/kimi" <<'FAKE'
471
473
  { echo "args:$*"; echo "prelaunch:${PRELAUNCH_RAN:-0}"; echo "extra:${CAIRN_EXTRA_SETTINGS:-}"; } > "$KIMI_LOG"
472
474
  exit "${FAKE_EXIT:-0}"
473
475
  FAKE
474
- chmod +x "$tmp/bin/claude" "$tmp/bin/opencode" "$tmp/bin/kimi"
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"
475
482
  ln -s "$ROOT/bin/cairn" "$tmp/bin/cairn"
476
483
  export PATH="$tmp/bin:$PATH"
477
484
  export CLAUDE_LOG="$tmp/claude.log"
478
485
  export OPENCODE_LOG="$tmp/opencode.log"
479
486
  export KIMI_LOG="$tmp/kimi.log"
487
+ export QWEN_LOG="$tmp/qwen.log"
480
488
 
481
489
  # Claude sync has three exact installation states. Normal sync and an explicit
482
490
  # overlay request with the master off must contain no capability hook bytes or
@@ -540,6 +548,10 @@ grep -qx "capability-registration:0" "$OPENCODE_LOG" || fail "OpenCode baseline
540
548
  grep -qx "args:--foo bar" "$KIMI_LOG" || fail "Kimi baseline did not pass args through"
541
549
  grep -qx "prelaunch:0" "$KIMI_LOG" || fail "Kimi prelaunch marker leaked without a hook"
542
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
+
543
555
  # Invalid/off master values stay on the legacy direct-exec path: one harness
544
556
  # process, unchanged argv/config environment, and no isolated assets or store.
545
557
  legacy_claude="$tmp/legacy-claude"
@@ -636,6 +648,12 @@ grep -qx "args:--plan" "$KIMI_LOG" || fail "Kimi pre-launch changed harness argv
636
648
  grep -qx "prelaunch:1" "$KIMI_LOG" || fail "Kimi pre-launch env not visible to harness"
637
649
  grep -qx "extra:$tmp/settings.json" "$KIMI_LOG" || fail "Kimi pre-launch environment was not preserved"
638
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
+
639
657
  # 3. pre-launch.sh non-zero aborts the launch before the harness runs.
640
658
  rm -f "$CLAUDE_LOG"
641
659
  cat > "$repo/.ai/pre-launch.sh" <<'EOF'
@@ -647,6 +665,9 @@ if "$launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort the
647
665
  rm -f "$KIMI_LOG"
648
666
  if "$kimi_launcher" >/dev/null 2>&1; then fail "non-zero pre-launch should abort Kimi"; fi
649
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"
650
671
  rm -f "$repo/.ai/pre-launch.sh"
651
672
 
652
673
  # 4. post-exit.sh runs after the harness with $CAIRN_EXIT_STATUS.
@@ -666,6 +687,13 @@ status=$?
666
687
  grep -qx "args:--auto" "$KIMI_LOG" || fail "Kimi launcher changed arguments"
667
688
  grep -qx "post:8" "$tmp/post.log" || fail "Kimi launcher post-exit status missing"
668
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
+
669
697
  # 5. Pi launcher preserves the same env/pre/post seams and passes arguments
670
698
  # unchanged; Pi has no generic settings-file flag so CAIRN_EXTRA_SETTINGS is ignored.
671
699
  rm -f "$repo/.ai/pre-launch.sh" "$repo/.ai/post-exit.sh"
@@ -44,6 +44,7 @@ bootstrap_output=$(cairn bootstrap "$tmp/project")
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
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"
47
48
  [[ -f "$tmp/project/.planning/config.json" ]] || fail "bootstrap did not install the planning scaffold"
48
49
 
49
50
  (cd "$tmp/project" && cairn doctor) >/dev/null || fail "installed package failed cairn doctor"
@@ -223,6 +224,8 @@ NODE
223
224
  fail "npm tarball omitted the Pi launcher template"
224
225
  [[ -f "$installed_root/templates/start-kimi.sh.template" ]] || \
225
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"
226
229
  [[ -f "$installed_root/schemas/note.schema.json" ]] || \
227
230
  fail "npm tarball omitted the note schema"
228
231
  [[ -f "$installed_root/schemas/memory-node.schema.json" ]] || \
@@ -40,6 +40,7 @@ PROJECT_AI_FILES=(
40
40
  start-opencode.sh
41
41
  start-pi.sh
42
42
  start-kimi.sh
43
+ start-qwen.sh
43
44
  env.example
44
45
  trajectory-redaction.json
45
46
  capabilities.json
@@ -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 "$@"