agent-trellis 0.8.0 → 0.9.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/README.md +45 -206
- package/dist/adapters/zcode.d.ts +21 -0
- package/dist/adapters/zcode.js +234 -0
- package/dist/cli.js +119 -10
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +4 -1
- package/dist/commands/mcp.d.ts +56 -14
- package/dist/commands/mcp.js +105 -28
- package/dist/commands/mcpGateway.js +2 -1
- package/dist/commands/mcpImport.d.ts +21 -2
- package/dist/commands/mcpImport.js +30 -7
- package/dist/commands/memory.d.ts +13 -1
- package/dist/commands/memory.js +15 -0
- package/dist/commands/migrate.js +4 -1
- package/dist/commands/onboard.js +15 -4
- package/dist/commands/remoteSkill.d.ts +90 -0
- package/dist/commands/remoteSkill.js +392 -0
- package/dist/commands/rollback.js +55 -1
- package/dist/commands/secretsAudit.js +4 -1
- package/dist/commands/skill.d.ts +37 -6
- package/dist/commands/skill.js +73 -15
- package/dist/commands/sync.js +3 -1
- package/dist/core/adapter.d.ts +11 -1
- package/dist/core/canonical.d.ts +17 -1
- package/dist/core/canonical.js +54 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +1 -0
- package/dist/lib/agentBridge.d.ts +169 -0
- package/dist/lib/agentBridge.js +367 -0
- package/dist/lib/agentBridgeProvider.d.ts +14 -0
- package/dist/lib/agentBridgeProvider.js +95 -0
- package/dist/lib/backup.d.ts +42 -0
- package/dist/lib/backup.js +36 -3
- package/dist/lib/chatAgents.d.ts +42 -0
- package/dist/lib/chatAgents.js +59 -0
- package/dist/lib/dirDigest.d.ts +11 -0
- package/dist/lib/dirDigest.js +52 -0
- package/dist/lib/envVarNames.d.ts +3 -0
- package/dist/lib/envVarNames.js +19 -0
- package/dist/lib/installAgent.d.ts +1 -1
- package/dist/lib/installAgent.js +1 -1
- package/dist/lib/mcpMigrateRead.d.ts +1 -0
- package/dist/lib/mcpMigrateRead.js +19 -1
- package/dist/lib/realHomeSnapshot.js +5 -0
- package/dist/lib/remoteSkillLock.d.ts +26 -0
- package/dist/lib/remoteSkillLock.js +88 -0
- package/dist/lib/remoteSkillSource.d.ts +45 -0
- package/dist/lib/remoteSkillSource.js +206 -0
- package/dist/lib/runtimeControlProvider.js +2 -0
- package/dist/lib/taskStore.js +1 -1
- package/dist/lib/terminalPicker.js +33 -6
- package/dist/lib/zcodeOwnership.d.ts +17 -0
- package/dist/lib/zcodeOwnership.js +37 -0
- package/dist/pi-bridge/bundle.js +2 -1
- package/dist/probes/codex.d.ts +7 -0
- package/dist/probes/codex.js +37 -5
- package/dist/probes/zcode.d.ts +32 -0
- package/dist/probes/zcode.js +140 -0
- package/docs/architecture.md +9 -5
- package/docs/assets/brand-v3/trellis-mark-v3-a.png +0 -0
- package/docs/assets/brand-v3/trellis-mark-v3-a.svg +10 -0
- package/docs/assets/poster-series-v3/README.md +60 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-en.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-en.svg +51 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-zh.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-zh.svg +51 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-en.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-en.svg +48 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-zh.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-zh.svg +48 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-en.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-en.svg +56 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-zh.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-zh.svg +56 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-en.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-en.svg +47 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-zh.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-zh.svg +47 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-en.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-en.svg +51 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-zh.png +0 -0
- package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-zh.svg +51 -0
- package/docs/getting-started.md +244 -14
- package/docs/overview.md +6 -3
- package/docs/overview.zh-CN.md +5 -3
- package/docs/roadmap.md +2 -1
- package/docs/video/README.md +76 -0
- package/docs/video/build/audio/01-opening.aiff +0 -0
- package/docs/video/build/audio/01-opening.mp3 +0 -0
- package/docs/video/build/audio/02-pain.aiff +0 -0
- package/docs/video/build/audio/02-pain.mp3 +0 -0
- package/docs/video/build/audio/03-solution.aiff +0 -0
- package/docs/video/build/audio/03-solution.mp3 +0 -0
- package/docs/video/build/audio/04-install.aiff +0 -0
- package/docs/video/build/audio/04-install.mp3 +0 -0
- package/docs/video/build/audio/04-setup.mp3 +0 -0
- package/docs/video/build/audio/05-dryrun.mp3 +0 -0
- package/docs/video/build/audio/05-init.aiff +0 -0
- package/docs/video/build/audio/05-init.mp3 +0 -0
- package/docs/video/build/audio/05-interactive.mp3 +0 -0
- package/docs/video/build/audio/06-dryrun.aiff +0 -0
- package/docs/video/build/audio/06-dryrun.mp3 +0 -0
- package/docs/video/build/audio/06-onboard.mp3 +0 -0
- package/docs/video/build/audio/06-run.mp3 +0 -0
- package/docs/video/build/audio/07-onboard.aiff +0 -0
- package/docs/video/build/audio/07-onboard.mp3 +0 -0
- package/docs/video/build/audio/07-verify.mp3 +0 -0
- package/docs/video/build/audio/08-closing.mp3 +0 -0
- package/docs/video/build/audio/08-doctor.aiff +0 -0
- package/docs/video/build/audio/08-doctor.mp3 +0 -0
- package/docs/video/build/audio/09-rollback.aiff +0 -0
- package/docs/video/build/audio/09-rollback.mp3 +0 -0
- package/docs/video/build/audio/10-principles.aiff +0 -0
- package/docs/video/build/audio/10-principles.mp3 +0 -0
- package/docs/video/build/audio/11-closing.aiff +0 -0
- package/docs/video/build/audio/11-closing.mp3 +0 -0
- package/docs/video/build/frames/01-opening-0.png +0 -0
- package/docs/video/build/frames/02-pain-1.png +0 -0
- package/docs/video/build/frames/02-pain-2.png +0 -0
- package/docs/video/build/frames/03-solution-3.png +0 -0
- package/docs/video/build/frames/04-install-4.png +0 -0
- package/docs/video/build/frames/04-setup-4.png +0 -0
- package/docs/video/build/frames/04-setup-5.png +0 -0
- package/docs/video/build/frames/05-dryrun-6.png +0 -0
- package/docs/video/build/frames/05-init-5.png +0 -0
- package/docs/video/build/frames/05-interactive-5.png +0 -0
- package/docs/video/build/frames/05-interactive-6.png +0 -0
- package/docs/video/build/frames/06-dryrun-6.png +0 -0
- package/docs/video/build/frames/06-dryrun-7.png +0 -0
- package/docs/video/build/frames/06-onboard-7.png +0 -0
- package/docs/video/build/frames/06-onboard-8.png +0 -0
- package/docs/video/build/frames/06-run-7.png +0 -0
- package/docs/video/build/frames/06-run-8.png +0 -0
- package/docs/video/build/frames/07-onboard-10.png +0 -0
- package/docs/video/build/frames/07-onboard-7.png +0 -0
- package/docs/video/build/frames/07-onboard-8.png +0 -0
- package/docs/video/build/frames/07-onboard-9.png +0 -0
- package/docs/video/build/frames/07-verify-10.png +0 -0
- package/docs/video/build/frames/07-verify-8.png +0 -0
- package/docs/video/build/frames/07-verify-9.png +0 -0
- package/docs/video/build/frames/08-closing-10.png +0 -0
- package/docs/video/build/frames/08-closing-11.png +0 -0
- package/docs/video/build/frames/08-doctor-11.png +0 -0
- package/docs/video/build/frames/08-doctor-9.png +0 -0
- package/docs/video/build/frames/09-rollback-10.png +0 -0
- package/docs/video/build/frames/09-rollback-12.png +0 -0
- package/docs/video/build/frames/10-principles-11.png +0 -0
- package/docs/video/build/frames/10-principles-13.png +0 -0
- package/docs/video/build/frames/11-closing-12.png +0 -0
- package/docs/video/build/frames/11-closing-14.png +0 -0
- package/docs/video/build/segments/01-opening.mp4 +0 -0
- package/docs/video/build/segments/01-opening.txt +3 -0
- package/docs/video/build/segments/02-pain.mp4 +0 -0
- package/docs/video/build/segments/02-pain.txt +5 -0
- package/docs/video/build/segments/03-solution.mp4 +0 -0
- package/docs/video/build/segments/03-solution.txt +3 -0
- package/docs/video/build/segments/04-install.mp4 +0 -0
- package/docs/video/build/segments/04-install.txt +3 -0
- package/docs/video/build/segments/04-setup.mp4 +0 -0
- package/docs/video/build/segments/04-setup.txt +3 -0
- package/docs/video/build/segments/05-dryrun.mp4 +0 -0
- package/docs/video/build/segments/05-dryrun.txt +3 -0
- package/docs/video/build/segments/05-init.mp4 +0 -0
- package/docs/video/build/segments/05-init.txt +3 -0
- package/docs/video/build/segments/05-interactive.mp4 +0 -0
- package/docs/video/build/segments/05-interactive.txt +5 -0
- package/docs/video/build/segments/06-dryrun.mp4 +0 -0
- package/docs/video/build/segments/06-dryrun.txt +3 -0
- package/docs/video/build/segments/06-onboard.mp4 +0 -0
- package/docs/video/build/segments/06-onboard.txt +5 -0
- package/docs/video/build/segments/06-run.mp4 +0 -0
- package/docs/video/build/segments/06-run.txt +3 -0
- package/docs/video/build/segments/07-onboard.mp4 +0 -0
- package/docs/video/build/segments/07-onboard.txt +5 -0
- package/docs/video/build/segments/07-verify.mp4 +0 -0
- package/docs/video/build/segments/07-verify.txt +5 -0
- package/docs/video/build/segments/08-closing.mp4 +0 -0
- package/docs/video/build/segments/08-closing.txt +3 -0
- package/docs/video/build/segments/08-doctor.mp4 +0 -0
- package/docs/video/build/segments/08-doctor.txt +3 -0
- package/docs/video/build/segments/09-rollback.mp4 +0 -0
- package/docs/video/build/segments/09-rollback.txt +3 -0
- package/docs/video/build/segments/10-principles.mp4 +0 -0
- package/docs/video/build/segments/10-principles.txt +3 -0
- package/docs/video/build/segments/11-closing.mp4 +0 -0
- package/docs/video/build/segments/11-closing.txt +3 -0
- package/docs/video/build/segments/final.txt +8 -0
- package/docs/video/build-ai/audio/01-hook.mp3 +0 -0
- package/docs/video/build-ai/audio/02-pain.mp3 +0 -0
- package/docs/video/build-ai/audio/03-solution.mp3 +0 -0
- package/docs/video/build-ai/audio/04-how.mp3 +0 -0
- package/docs/video/build-ai/audio/05-safe.mp3 +0 -0
- package/docs/video/build-ai/audio/06-closing.mp3 +0 -0
- package/docs/video/build-ai/clips/01-hook.mp4 +0 -0
- package/docs/video/build-ai/clips/02-pain.mp4 +0 -0
- package/docs/video/build-ai/clips/03-solution.mp4 +0 -0
- package/docs/video/build-ai/clips/04-how.mp4 +0 -0
- package/docs/video/build-ai/clips/05-safe.mp4 +0 -0
- package/docs/video/build-ai/clips/06-closing.mp4 +0 -0
- package/docs/video/build-ai/first-frame-02-pain.png +0 -0
- package/docs/video/build-ai/first-frame-03-solution.png +0 -0
- package/docs/video/build-ai/first-frame-04-how.png +0 -0
- package/docs/video/build-ai/first-frame-05-safe.png +0 -0
- package/docs/video/build-ai/segments/01-hook.caption.txt +2 -0
- package/docs/video/build-ai/segments/01-hook.mp4 +0 -0
- package/docs/video/build-ai/segments/02-pain.mp4 +0 -0
- package/docs/video/build-ai/segments/03-solution.mp4 +0 -0
- package/docs/video/build-ai/segments/04-how.mp4 +0 -0
- package/docs/video/build-ai/segments/05-safe.mp4 +0 -0
- package/docs/video/build-ai/segments/06-closing.mp4 +0 -0
- package/docs/video/build-ai/segments/cap-01-hook.png +0 -0
- package/docs/video/build-ai/segments/cap-02-pain.png +0 -0
- package/docs/video/build-ai/segments/cap-03-solution.png +0 -0
- package/docs/video/build-ai/segments/cap-04-how.png +0 -0
- package/docs/video/build-ai/segments/cap-05-safe.png +0 -0
- package/docs/video/build-ai/segments/cap-06-closing.png +0 -0
- package/docs/video/build-ai/segments/final.txt +6 -0
- package/docs/video/build-posters/audio/01-hook.mp3 +0 -0
- package/docs/video/build-posters/audio/02-pain.mp3 +0 -0
- package/docs/video/build-posters/audio/03-solution.mp3 +0 -0
- package/docs/video/build-posters/audio/04-how.mp3 +0 -0
- package/docs/video/build-posters/audio/05-safe.mp3 +0 -0
- package/docs/video/build-posters/audio/06-closing.mp3 +0 -0
- package/docs/video/build-posters/cap-01-hook.png +0 -0
- package/docs/video/build-posters/cap-02-pain.png +0 -0
- package/docs/video/build-posters/cap-03-solution.png +0 -0
- package/docs/video/build-posters/cap-04-how.png +0 -0
- package/docs/video/build-posters/cap-05-safe.png +0 -0
- package/docs/video/build-posters/cap-06-closing.png +0 -0
- package/docs/video/build-posters/card-01-hook.png +0 -0
- package/docs/video/build-posters/card-06-closing.png +0 -0
- package/docs/video/build-posters/segments/01-hook.mp4 +0 -0
- package/docs/video/build-posters/segments/02-pain.mp4 +0 -0
- package/docs/video/build-posters/segments/03-solution.mp4 +0 -0
- package/docs/video/build-posters/segments/04-how.mp4 +0 -0
- package/docs/video/build-posters/segments/05-safe.mp4 +0 -0
- package/docs/video/build-posters/segments/06-closing.mp4 +0 -0
- package/docs/video/build-posters/segments/final.txt +6 -0
- package/docs/video/generate-video-ai.mjs +334 -0
- package/docs/video/generate-video-posters.mjs +204 -0
- package/docs/video/generate-video.mjs +426 -0
- package/docs/video/outline.zh-CN.md +102 -0
- package/docs/video/trellis-demo-video-ai.mp4 +0 -0
- package/docs/video/trellis-demo-video.mp4 +0 -0
- package/docs/video/voiceover.zh-CN.md +49 -0
- package/package.json +5 -2
- package/schema/servers.example.yaml +1 -1
- package/docs/assets/agent-trellis-architecture-horizontal.svg +0 -80
- package/docs/assets/agent-trellis-architecture-vertical.svg +0 -82
- package/docs/assets/trellis-mark.svg +0 -12
package/README.md
CHANGED
|
@@ -1,237 +1,76 @@
|
|
|
1
1
|
# Trellis
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="docs/assets/trellis-mark.svg" width="88" alt="Trellis logo" />
|
|
4
|
+
<img src="docs/assets/brand-v3/trellis-mark-v3-a.svg" width="88" alt="Trellis logo" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center"><strong>Unified Code Agent Runtime</strong></p>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
One canonical source for Skills, MCP, Memory, and shared instructions —
|
|
10
|
+
projected safely into Claude Code, Codex, Kiro, pi, Kimi Code, and ZCode.
|
|
11
11
|
|
|
12
|
-
>
|
|
13
|
-
>
|
|
12
|
+
> 中文:一个 canonical source 统一管理多个 Code Agent 的 Skill、MCP、Memory
|
|
13
|
+
> 与共享指令,支持预览、备份与回滚。
|
|
14
14
|
|
|
15
|
-
Trellis
|
|
16
|
-
source and generates verified native or Runtime projections from it.
|
|
15
|
+

|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-

|
|
21
|
-
|
|
22
|
-
## Install
|
|
17
|
+
## Quick start
|
|
23
18
|
|
|
24
19
|
```bash
|
|
25
20
|
npm install -g agent-trellis
|
|
26
|
-
```
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
trellis onboard --dry-run # preview: migration source, managed agents, MCP, memory
|
|
23
|
+
trellis onboard # apply — every write is backed up automatically
|
|
24
|
+
trellis doctor # verify state, any time
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
trellis
|
|
32
|
-
trellis onboard
|
|
33
|
-
trellis doctor
|
|
26
|
+
# import a GitHub Skill into Trellis canonical storage, then sync it
|
|
27
|
+
npx trellis add mattpocock/skills --skill loop-me --dry-run
|
|
34
28
|
```
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
Memory before applying. See [`docs/overview.md`](docs/overview.md) for the
|
|
38
|
-
short product explanation and [`docs/getting-started.md`](docs/getting-started.md)
|
|
39
|
-
for the complete workflow.
|
|
30
|
+
Something went wrong? `trellis rollback` inverts the last run.
|
|
40
31
|
|
|
41
|
-
##
|
|
32
|
+
## How it compares
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
| | Skills / Rules | MCP | Memory | Secret hygiene | Agents covered |
|
|
35
|
+
|---|---|---|---|---|---|
|
|
36
|
+
| [ai-rules](https://github.com/block/ai-rules), [skills-link](https://github.com/shanliuling/skills-link), [agent_sync](https://github.com/yelmuratoff/agent_sync) | ✅ | ❌ | ❌ | ❌ | Many — but not Kiro or pi |
|
|
37
|
+
| [skillshare](https://github.com/runkids/skillshare), [skills-hub](https://github.com/qufei1993/skills-hub) | ✅ skills only | ❌ | ❌ | ❌ | Codex, Claude, … |
|
|
38
|
+
| [mcp-router](https://github.com/mcp-router/mcp-router) | ❌ | ✅ desktop router | ❌ | config in its own dashboard | — |
|
|
39
|
+
| [MetaMCP](https://github.com/metatool-ai/metamcp), [mcp-hub](https://github.com/ravitemer/mcp-hub) | ❌ | ✅ aggregator | ❌ | ❌ | — |
|
|
40
|
+
| [cli-agent-orchestrator](https://github.com/awslabs/cli-agent-orchestrator) | ❌ | ❌ | ❌ | ❌ | Runs agent fleets, doesn't unify config |
|
|
41
|
+
| **Trellis** | ✅ | ✅ native config, Runtime, or hub mode | ✅ shared memory + handoffs | ✅ references only, `secrets audit` | Claude Code, Codex, Kiro, pi, Kimi Code, ZCode |
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
walkthrough — example output for each command, what each `migrate`/`sync`
|
|
58
|
-
conflict action means, and troubleshooting.
|
|
59
|
-
|
|
60
|
-
**One command:**
|
|
43
|
+
Sync tools stop at rules and skills. MCP routers manage servers but hold their
|
|
44
|
+
own config — a second source of truth (we hit real token drift with exactly
|
|
45
|
+
that setup). Trellis is the only one treating Skills + MCP + Memory +
|
|
46
|
+
instructions as **one** canonical system, and the only one covering Kiro and
|
|
47
|
+
pi. Full landscape survey: [`docs/research.md`](docs/research.md).
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
trellis onboard
|
|
64
|
-
```
|
|
49
|
+
## Design principles
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`--manage none` to skip the prompt). The source is **not** managed by
|
|
73
|
-
default — importing from Claude Code doesn't mean Trellis starts writing to
|
|
74
|
-
Claude Code too, unless you explicitly include it. Selecting an agent that
|
|
75
|
-
isn't installed yet (e.g. pi) is itself the authorization to install it —
|
|
76
|
-
one confirmation, then a real `npm install -g <package>`; Kiro has no CLI
|
|
77
|
-
package and is refused with its download URL instead. Once resolved, it
|
|
78
|
-
runs `migrate`, `sync`, `mcp sync`, and `secrets audit` against the managed
|
|
79
|
-
set — the whole onboarding path in one command, no follow-up commands to
|
|
80
|
-
type by hand. If no agent is detected, it prints each supported agent's
|
|
81
|
-
real install command/URL and stops — it never installs anything on its own
|
|
82
|
-
initiative. Add `--dry-run` to preview the whole thing with zero writes.
|
|
83
|
-
|
|
84
|
-
Onboarding only adds to the existing managed set. To deliberately narrow or
|
|
85
|
-
replace Trellis's future write boundary without touching an agent's current
|
|
86
|
-
native state, use `trellis manage list|set|add|remove`; these mutations are
|
|
87
|
-
dry-runnable and backed by `trellis rollback`.
|
|
51
|
+
1. **One canonical source, many adapters** — agent-native configs are
|
|
52
|
+
generated artifacts, never hand-edited.
|
|
53
|
+
2. **No plaintext secrets, ever** — outputs hold variable references;
|
|
54
|
+
`trellis secrets audit` rejects raw values.
|
|
55
|
+
3. **Verify, don't assume** — every adapter claim is backed by an executed
|
|
56
|
+
probe in isolated Docker sandboxes, never a developer's own dotfiles.
|
|
88
57
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Trellis itself as a Skill
|
|
94
|
-
|
|
95
|
-
Trellis ships one package-owned Skill, `trellis-runtime`, with two layers:
|
|
96
|
-
|
|
97
|
-
| Layer | Who uses it | What it covers |
|
|
98
|
-
| --- | --- | --- |
|
|
99
|
-
| Takeover and migration | the operator, with Agent assistance | inspect, dry-run, choose a migration source, choose the managed boundary, select Skills/MCP/Memory, authorize OAuth, sync, audit, verify, and rollback |
|
|
100
|
-
| Runtime consumption | every managed Agent | discover and read canonical Skills, inspect shared Memory and global instructions, diagnose MCP, see managed Agents, and inspect or explicitly confirm task handoffs |
|
|
101
|
-
|
|
102
|
-
`trellis init` bootstraps this Skill into
|
|
103
|
-
`~/.trellis/skills/trellis-runtime/SKILL.md`. After an Agent is managed,
|
|
104
|
-
`trellis sync` delivers it in the Agent's native Skill location for native or
|
|
105
|
-
`both` delivery. For Runtime-only delivery, `SkillProvider` exposes the same
|
|
106
|
-
canonical Skill through the `trellis` MCP Runtime; Kimi Code uses
|
|
107
|
-
`trellis kimi` to prevent a second native Skill discovery path. This is one
|
|
108
|
-
canonical Skill, not a copied per-Agent variant.
|
|
109
|
-
|
|
110
|
-
Before takeover, the Skill cannot be consumed from an Agent that has not been
|
|
111
|
-
connected to Trellis yet; use the CLI and
|
|
112
|
-
[`docs/getting-started.md`](docs/getting-started.md#trellis-runtime-skill)
|
|
113
|
-
for the first-run path. After takeover, the Skill is the Agent's operating
|
|
114
|
-
guide for Trellis-managed capabilities. The MCP client may display names such
|
|
115
|
-
as `mcp__trellis__skills_search`; `mcp__` is client-generated, while Trellis's
|
|
116
|
-
portable names are `skills_search`, `memory_search`, `runtime_status`, and so
|
|
117
|
-
on.
|
|
118
|
-
|
|
119
|
-
**Or step by step** (what `onboard` is actually doing under the hood, if you
|
|
120
|
-
want to run any one stage on its own):
|
|
121
|
-
|
|
122
|
-
For standard MCP Router/client exports, use `trellis mcp import <json-file>`.
|
|
123
|
-
It reads `mcpServers` without changing the source, de-duplicates existing
|
|
124
|
-
servers, extracts credential-like environment values into the ignored local
|
|
125
|
-
secret file, converts supported `mcp-remote` Basic Auth entries to native HTTP,
|
|
126
|
-
and reports unsupported inline credentials or unavailable paths.
|
|
127
|
-
Always run it with `--dry-run` first; see the detailed importer rules in
|
|
128
|
-
[`docs/getting-started.md`](docs/getting-started.md).
|
|
129
|
-
|
|
130
|
-
After upgrading Trellis, refresh the package-owned Runtime Skill with
|
|
131
|
-
`trellis skill update-builtin --dry-run` followed by
|
|
132
|
-
`trellis skill update-builtin`; the operation is backed up and does not require
|
|
133
|
-
editing Agent-native files.
|
|
134
|
-
|
|
135
|
-
**Already using Claude Code, Codex, Kiro, or pi and want to migrate what you
|
|
136
|
-
already have?**
|
|
137
|
-
|
|
138
|
-
1. `trellis init` — creates `~/.trellis/` with a minimal skeleton (only what's
|
|
139
|
-
missing; never overwrites a file you already have) and tells you which of
|
|
140
|
-
the five supported agents it found on this machine.
|
|
141
|
-
2. `trellis migrate --from <agent>` — once per agent you already use. Copies
|
|
142
|
-
that agent's real skills and instructions into canonical source. Never
|
|
143
|
-
overwrites: an already-identical skill is reported and skipped, a genuine
|
|
144
|
-
conflict is reported and left for you to resolve by hand. Add `--dry-run`
|
|
145
|
-
to preview first.
|
|
146
|
-
3. `trellis sync` — distributes canonical skills/instructions to every
|
|
147
|
-
**managed** agent (`~/.trellis/managed.yaml` — empty by default; edit it
|
|
148
|
-
by hand or let `trellis onboard` write it). An agent that's merely present
|
|
149
|
-
but not managed is never touched. Add `--dry-run` to preview first.
|
|
150
|
-
4. `trellis mcp sync` — distributes `~/.trellis/mcp/servers.yaml` (see
|
|
151
|
-
[`schema/servers.example.yaml`](schema/servers.example.yaml)) to every
|
|
152
|
-
managed agent's native MCP config, including ownership-safe removal.
|
|
153
|
-
5. `trellis secrets audit` — fails non-zero if any managed agent's real
|
|
154
|
-
config holds a literal credential or an unexpected env var name.
|
|
155
|
-
6. `trellis doctor` — read-only scan of every present agent's current state
|
|
156
|
-
(managed or not); run any time to check for drift.
|
|
157
|
-
|
|
158
|
-
**Starting from nothing?** Skip step 2 — `trellis init`'s placeholder
|
|
159
|
-
`agents.md` and empty `skills/` are a fine starting point; edit them by hand.
|
|
160
|
-
|
|
161
|
-
**Every real write `sync`/`mcp sync`/`onboard` perform is backed up
|
|
162
|
-
first, automatically — no flag to opt in.** Before creating, repairing,
|
|
163
|
-
or removing anything, each is recorded to a structured, timestamped run
|
|
164
|
-
under `~/.trellis/backups/`, with enough information to invert it exactly.
|
|
165
|
-
`trellis rollback` undoes one recorded run — omit a run id for the most
|
|
166
|
-
recent one:
|
|
58
|
+
Trellis aligns with the emerging [`.agents Protocol`](https://dotagentsprotocol.com)
|
|
59
|
+
draft rather than inventing another standard.
|
|
167
60
|
|
|
168
|
-
|
|
169
|
-
$ trellis rollback
|
|
170
|
-
rollback 2026-09-13T04-52-18-727Z-mcp-sync
|
|
171
|
-
✅ 3 restored, 0 conflict(s), 0 already reverted
|
|
172
|
-
- [restore] restore /Users/you/.claude.json to its content before this run (...)
|
|
173
|
-
```
|
|
61
|
+
## Docs
|
|
174
62
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
restore with zero writes.
|
|
63
|
+
- [Overview](docs/overview.md) ([中文](docs/overview.zh-CN.md)) — what it is and why
|
|
64
|
+
- [Getting started](docs/getting-started.md) — every command, with example output
|
|
65
|
+
- [Architecture](docs/architecture.md) — what we build vs. deliberately reuse
|
|
66
|
+
- [Research](docs/research.md) — the full competitive landscape
|
|
180
67
|
|
|
181
68
|
## Status
|
|
182
69
|
|
|
183
|
-
**Early, pre-1.0.**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
sandbox matrix covers migration, steady-state management, runtime routing,
|
|
188
|
-
failure isolation, and expected conflicts. A separate agent image installs
|
|
189
|
-
real Codex, Claude Code, Kiro CLI, and pi binaries to verify their isolated
|
|
190
|
-
configuration behavior. `onboard` picks one agent as the migration base when
|
|
191
|
-
more than one is present; merging differing content across multiple agents
|
|
192
|
-
into one result is named future work, not built yet.
|
|
193
|
-
|
|
194
|
-
**Known limitations, honestly stated rather than discovered the hard way:**
|
|
195
|
-
- MCP servers are never spawned/handshake-tested by `trellis mcp sync` or
|
|
196
|
-
`trellis sync` — only that the *config* is written correctly.
|
|
197
|
-
`trellis doctor --probe-mcp` is the one command that actually connects,
|
|
198
|
-
and it's opt-in.
|
|
199
|
-
- The pi bridge extension (`trellis-pi-mcp-bridge`) has been verified to
|
|
200
|
-
load and register tools without erroring, never against a real LLM tool
|
|
201
|
-
call in production.
|
|
202
|
-
- Verification has run against real Docker containers and real isolated
|
|
203
|
-
Codex, Claude Code, Kiro CLI, and pi installs — not against a developer's
|
|
204
|
-
actual daily-use agent directories. If you hit something a clean-room
|
|
205
|
-
sandbox wouldn't have caught, please open an issue.
|
|
206
|
-
- `~/.trellis/backups/` has no automatic pruning yet — every `sync`/
|
|
207
|
-
`mcp sync`/`onboard`/`mcp import` run that performs a real write adds one more run
|
|
208
|
-
directory, with no cap. Delete old ones by hand for now.
|
|
209
|
-
|
|
210
|
-
A canonical MCP server can also declare `enabled: false` (kept defined,
|
|
211
|
-
never written to any agent) and `static_env` (a value that was never a
|
|
212
|
-
secret — an email, an environment tag — written verbatim instead of
|
|
213
|
-
resolved by name; still scanned against `reject_patterns` like every
|
|
214
|
-
other literal field). Before ever writing a name-only `env` entry,
|
|
215
|
-
`mcp sync` now checks it actually resolves through the same source
|
|
216
|
-
`secrets audit` and the pi bridge use — an unresolvable name is refused
|
|
217
|
-
as a conflict, not silently written and left to break that server's
|
|
218
|
-
connection once the agent starts it. Both found via real-machine
|
|
219
|
-
dogfooding, not a synthetic fixture — see
|
|
220
|
-
[`schema/servers.example.yaml`](schema/servers.example.yaml).
|
|
221
|
-
|
|
222
|
-
## Design principles
|
|
223
|
-
|
|
224
|
-
1. **One canonical source, many adapters.** Each agent's native config file is
|
|
225
|
-
a generated or symlinked artifact, never hand-edited.
|
|
226
|
-
2. **Reuse before building.** MCP aggregation, memory, and secret-reference
|
|
227
|
-
patterns already have mature open source answers — Trellis wires to them
|
|
228
|
-
instead of reimplementing them. See [`docs/architecture.md`](docs/architecture.md).
|
|
229
|
-
3. **No plaintext secrets, ever.** Every adapter output holds variable
|
|
230
|
-
references only; `trellis secrets audit` scans for and rejects raw values.
|
|
231
|
-
4. **Verify, don't assume.** Every claim this project makes about an agent's
|
|
232
|
-
behavior (file format, discovery path, MCP support) is backed by an
|
|
233
|
-
executed probe, not documentation-reading. Adapters that can't be verified
|
|
234
|
-
this way don't ship.
|
|
70
|
+
**Early, pre-1.0.** All CLI commands are implemented, unit-tested, and verified
|
|
71
|
+
against real agents in isolated containers. Known limitations are listed in
|
|
72
|
+
[Getting started](docs/getting-started.md); please open an issue for anything
|
|
73
|
+
a clean-room sandbox wouldn't catch.
|
|
235
74
|
|
|
236
75
|
## License
|
|
237
76
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-first ZCode adapter.
|
|
3
|
+
*
|
|
4
|
+
* ZCode's native `~/.agents/skills` scan prevents scope-safe native delivery,
|
|
5
|
+
* so Runtime-only delivery uses one MCP Runtime edge and turns off the two
|
|
6
|
+
* native Skill switches through a ledger-backed, reversible config change.
|
|
7
|
+
*/
|
|
8
|
+
import type { AdapterPlanItem, AdapterProbeResult, AdapterVerifyResult, TrellisAdapter } from "../core/adapter.js";
|
|
9
|
+
import type { CanonicalSource } from "../core/types.js";
|
|
10
|
+
import type { BackupSession } from "../lib/backup.js";
|
|
11
|
+
export declare class ZcodeAdapter implements TrellisAdapter {
|
|
12
|
+
private readonly homeDir;
|
|
13
|
+
private readonly env;
|
|
14
|
+
readonly name = "ZCode";
|
|
15
|
+
readonly id: "zcode";
|
|
16
|
+
constructor(homeDir?: string, env?: NodeJS.ProcessEnv);
|
|
17
|
+
probe(): Promise<AdapterProbeResult>;
|
|
18
|
+
plan(canonical: CanonicalSource): Promise<AdapterPlanItem[]>;
|
|
19
|
+
apply(plan: AdapterPlanItem[], backup: BackupSession): Promise<void>;
|
|
20
|
+
verify(canonical: CanonicalSource): Promise<AdapterVerifyResult>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-first ZCode adapter.
|
|
3
|
+
*
|
|
4
|
+
* ZCode's native `~/.agents/skills` scan prevents scope-safe native delivery,
|
|
5
|
+
* so Runtime-only delivery uses one MCP Runtime edge and turns off the two
|
|
6
|
+
* native Skill switches through a ledger-backed, reversible config change.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { isInScope } from "../core/adapter.js";
|
|
12
|
+
import { capabilityDeliveryForAgent, usesNativeCapabilityDelivery } from "../core/types.js";
|
|
13
|
+
import { applySymlinkPlan, planSymlinks } from "./symlinkPlan.js";
|
|
14
|
+
import { renderJsonServerEntry } from "./jsonMcp.js";
|
|
15
|
+
import { LEGACY_GATEWAY_ENTRY_NAME, LEGACY_RUNTIME_ENTRY_NAME, resolveMcpPlan } from "./mcpPlan.js";
|
|
16
|
+
import { deepEqual } from "../lib/deepEqual.js";
|
|
17
|
+
import { forgetOwned, loadMcpOwnership, ownedByAgent, recordOwned, saveMcpOwnership } from "../lib/mcpOwnership.js";
|
|
18
|
+
import { forgetZcodeSettingsOwnership, loadZcodeSettingsOwnership, saveZcodeSettingsOwnership } from "../lib/zcodeOwnership.js";
|
|
19
|
+
import * as zcodeProbe from "../probes/zcode.js";
|
|
20
|
+
function readConfig(path) {
|
|
21
|
+
if (!existsSync(path))
|
|
22
|
+
return undefined;
|
|
23
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
24
|
+
}
|
|
25
|
+
function record(value) {
|
|
26
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
27
|
+
}
|
|
28
|
+
function zcodeServers(parsed) {
|
|
29
|
+
return record(record(parsed?.mcp).servers);
|
|
30
|
+
}
|
|
31
|
+
function planZcodeMcp(opts) {
|
|
32
|
+
const { desired, conflicts } = resolveMcpPlan("zcode", opts.mcp, opts.managedAgents, opts.policy);
|
|
33
|
+
const existing = zcodeServers(opts.parsed);
|
|
34
|
+
const items = [];
|
|
35
|
+
for (const { name, def } of desired) {
|
|
36
|
+
const rendered = renderJsonServerEntry(def);
|
|
37
|
+
if (deepEqual(existing[name], rendered))
|
|
38
|
+
continue;
|
|
39
|
+
items.push({
|
|
40
|
+
action: "create",
|
|
41
|
+
kind: "mcp",
|
|
42
|
+
target: opts.configPath,
|
|
43
|
+
mcpWrite: { name, def },
|
|
44
|
+
description: `MCP server "${name}" ${existing[name] === undefined ? "created" : "updated"} in ${opts.configPath}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
for (const conflict of conflicts) {
|
|
48
|
+
items.push({ action: "conflict", kind: "mcp", target: opts.configPath, description: conflict.message, ...(conflict.remediation ? { remediation: conflict.remediation } : {}) });
|
|
49
|
+
}
|
|
50
|
+
const desiredNames = new Set(desired.map((entry) => entry.name));
|
|
51
|
+
for (const [name, expected] of Object.entries(opts.ownership)) {
|
|
52
|
+
if (desiredNames.has(name))
|
|
53
|
+
continue;
|
|
54
|
+
if ((name === LEGACY_GATEWAY_ENTRY_NAME || name === LEGACY_RUNTIME_ENTRY_NAME) && desiredNames.has("trellis"))
|
|
55
|
+
continue;
|
|
56
|
+
if (existing[name] === undefined || !deepEqual(existing[name], expected))
|
|
57
|
+
continue;
|
|
58
|
+
items.push({
|
|
59
|
+
action: "remove",
|
|
60
|
+
kind: "mcp",
|
|
61
|
+
target: opts.configPath,
|
|
62
|
+
mcpRemove: { name },
|
|
63
|
+
description: `MCP server "${name}" removed from ${opts.configPath} — no longer in canonical`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return items;
|
|
67
|
+
}
|
|
68
|
+
function applyZcodeMcp(parsed, items) {
|
|
69
|
+
const base = parsed ?? {};
|
|
70
|
+
const mcp = record(base.mcp);
|
|
71
|
+
const servers = { ...record(mcp.servers) };
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
if (item.kind !== "mcp")
|
|
74
|
+
continue;
|
|
75
|
+
if (item.action === "create" && item.mcpWrite)
|
|
76
|
+
servers[item.mcpWrite.name] = renderJsonServerEntry(item.mcpWrite.def);
|
|
77
|
+
if (item.action === "remove" && item.mcpRemove)
|
|
78
|
+
delete servers[item.mcpRemove.name];
|
|
79
|
+
}
|
|
80
|
+
return { ...base, mcp: { ...mcp, servers } };
|
|
81
|
+
}
|
|
82
|
+
function currentSkillControls(parsed) {
|
|
83
|
+
const features = record(parsed?.features);
|
|
84
|
+
const skills = record(parsed?.skills);
|
|
85
|
+
return {
|
|
86
|
+
featuresSkill: typeof features.skill === "boolean" ? features.skill : null,
|
|
87
|
+
skillsEnabled: typeof skills.enabled === "boolean" ? skills.enabled : null,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function applySkillControls(parsed, controls) {
|
|
91
|
+
const features = { ...record(parsed.features) };
|
|
92
|
+
const skills = { ...record(parsed.skills) };
|
|
93
|
+
if (controls.featuresSkill === null)
|
|
94
|
+
delete features.skill;
|
|
95
|
+
else
|
|
96
|
+
features.skill = controls.featuresSkill;
|
|
97
|
+
if (controls.skillsEnabled === null)
|
|
98
|
+
delete skills.enabled;
|
|
99
|
+
else
|
|
100
|
+
skills.enabled = controls.skillsEnabled;
|
|
101
|
+
return { ...parsed, features, skills };
|
|
102
|
+
}
|
|
103
|
+
export class ZcodeAdapter {
|
|
104
|
+
homeDir;
|
|
105
|
+
env;
|
|
106
|
+
name = "ZCode";
|
|
107
|
+
id = "zcode";
|
|
108
|
+
constructor(homeDir = homedir(), env = process.env) {
|
|
109
|
+
this.homeDir = homeDir;
|
|
110
|
+
this.env = env;
|
|
111
|
+
}
|
|
112
|
+
async probe() {
|
|
113
|
+
const snapshot = await zcodeProbe.probe(this.homeDir, { env: this.env });
|
|
114
|
+
return { present: snapshot.present, version: snapshot.version, ...(snapshot.diagnostics.length ? { detail: snapshot.diagnostics.join("; ") } : {}) };
|
|
115
|
+
}
|
|
116
|
+
async plan(canonical) {
|
|
117
|
+
const profile = zcodeProbe.resolveZcodeProfile(this.homeDir, this.env);
|
|
118
|
+
if (!profile)
|
|
119
|
+
return [];
|
|
120
|
+
const parsed = readConfig(profile.configPath);
|
|
121
|
+
const canonicalRoot = dirname(canonical.instructionsFile);
|
|
122
|
+
const runtimeOnly = capabilityDeliveryForAgent(this.id, canonical.mcp) === "mcp";
|
|
123
|
+
const items = [];
|
|
124
|
+
items.push(...planSymlinks({
|
|
125
|
+
rootDir: join(this.homeDir, ".zcode", "skills"),
|
|
126
|
+
desired: usesNativeCapabilityDelivery(this.id, canonical.mcp)
|
|
127
|
+
? canonical.skills.filter((skill) => isInScope(this.id, skill.scope, canonical.managedAgents)).map((skill) => ({ name: skill.name, target: skill.dir }))
|
|
128
|
+
: [],
|
|
129
|
+
canonicalRoot: join(canonicalRoot, "skills"),
|
|
130
|
+
kind: "skill",
|
|
131
|
+
}));
|
|
132
|
+
items.push(...planSymlinks({
|
|
133
|
+
rootDir: join(this.homeDir, ".zcode"),
|
|
134
|
+
desired: [{ name: "AGENTS.md", target: canonical.instructionsFile }],
|
|
135
|
+
canonicalRoot,
|
|
136
|
+
kind: "instructions",
|
|
137
|
+
}));
|
|
138
|
+
items.push(...planZcodeMcp({
|
|
139
|
+
configPath: profile.configPath,
|
|
140
|
+
parsed,
|
|
141
|
+
mcp: canonical.mcp,
|
|
142
|
+
managedAgents: canonical.managedAgents,
|
|
143
|
+
policy: canonical.secretsPolicy,
|
|
144
|
+
ownership: ownedByAgent(loadMcpOwnership(this.homeDir), this.id),
|
|
145
|
+
}));
|
|
146
|
+
const current = currentSkillControls(parsed);
|
|
147
|
+
const ownership = loadZcodeSettingsOwnership(this.homeDir);
|
|
148
|
+
if (runtimeOnly && (current.featuresSkill !== false || current.skillsEnabled !== false)) {
|
|
149
|
+
items.push({
|
|
150
|
+
action: "create",
|
|
151
|
+
kind: "zcode-skill-control",
|
|
152
|
+
target: profile.configPath,
|
|
153
|
+
zcodeSkillControls: { featuresSkill: false, skillsEnabled: false, previous: current },
|
|
154
|
+
description: `native ZCode Skill discovery disabled in ${profile.configPath} for Runtime-only delivery`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
else if (!runtimeOnly && ownership?.configPath === profile.configPath && current.featuresSkill === false && current.skillsEnabled === false) {
|
|
158
|
+
items.push({
|
|
159
|
+
action: "remove",
|
|
160
|
+
kind: "zcode-skill-control",
|
|
161
|
+
target: profile.configPath,
|
|
162
|
+
zcodeSkillControls: { ...ownership.previous },
|
|
163
|
+
description: `previous native ZCode Skill settings restored in ${profile.configPath}`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return items;
|
|
167
|
+
}
|
|
168
|
+
async apply(plan, backup) {
|
|
169
|
+
await applySymlinkPlan(plan.filter((item) => item.kind === "skill" || item.kind === "instructions"), backup);
|
|
170
|
+
const configItems = plan.filter((item) => (item.kind === "mcp" || item.kind === "zcode-skill-control") && (item.action === "create" || item.action === "remove"));
|
|
171
|
+
if (configItems.length === 0)
|
|
172
|
+
return;
|
|
173
|
+
const configPath = configItems[0].target;
|
|
174
|
+
let merged = applyZcodeMcp(readConfig(configPath), configItems);
|
|
175
|
+
const control = configItems.find((item) => item.kind === "zcode-skill-control")?.zcodeSkillControls;
|
|
176
|
+
if (control)
|
|
177
|
+
merged = applySkillControls(merged, control);
|
|
178
|
+
backup.writeFile(configPath, `${JSON.stringify(merged, null, 2)}\n`);
|
|
179
|
+
let ownership = loadMcpOwnership(this.homeDir);
|
|
180
|
+
for (const item of configItems) {
|
|
181
|
+
if (item.kind !== "mcp")
|
|
182
|
+
continue;
|
|
183
|
+
if (item.action === "create" && item.mcpWrite)
|
|
184
|
+
ownership = recordOwned(ownership, this.id, item.mcpWrite.name, renderJsonServerEntry(item.mcpWrite.def));
|
|
185
|
+
if (item.action === "remove" && item.mcpRemove)
|
|
186
|
+
ownership = forgetOwned(ownership, this.id, item.mcpRemove.name);
|
|
187
|
+
}
|
|
188
|
+
saveMcpOwnership(this.homeDir, ownership);
|
|
189
|
+
const controlItem = configItems.find((item) => item.kind === "zcode-skill-control");
|
|
190
|
+
if (controlItem?.action === "create" && controlItem.zcodeSkillControls?.previous) {
|
|
191
|
+
saveZcodeSettingsOwnership(this.homeDir, {
|
|
192
|
+
configPath,
|
|
193
|
+
managed: { featuresSkill: false, skillsEnabled: false },
|
|
194
|
+
previous: controlItem.zcodeSkillControls.previous,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
else if (controlItem?.action === "remove") {
|
|
198
|
+
forgetZcodeSettingsOwnership(this.homeDir);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async verify(canonical) {
|
|
202
|
+
const snapshot = await zcodeProbe.probe(this.homeDir, { env: this.env });
|
|
203
|
+
if (!snapshot.present)
|
|
204
|
+
return { ok: false, mismatches: ["zcode is not present on this machine"] };
|
|
205
|
+
const profile = zcodeProbe.resolveZcodeProfile(this.homeDir, this.env);
|
|
206
|
+
if (!profile)
|
|
207
|
+
return { ok: false, mismatches: ["zcode profile cannot be selected"] };
|
|
208
|
+
const mismatches = [];
|
|
209
|
+
const desiredNativeSkills = canonical.skills
|
|
210
|
+
.filter((skill) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
211
|
+
.map((skill) => skill.name);
|
|
212
|
+
const actualNativeSkills = new Set(snapshot.skillRoots.filter((root) => root.path === join(this.homeDir, ".zcode", "skills")).flatMap((root) => root.skills.map((skill) => skill.name)));
|
|
213
|
+
for (const name of desiredNativeSkills)
|
|
214
|
+
if (!actualNativeSkills.has(name))
|
|
215
|
+
mismatches.push(`skill "${name}" is missing on ZCode`);
|
|
216
|
+
const expectedMcp = planZcodeMcp({
|
|
217
|
+
configPath: profile.configPath,
|
|
218
|
+
parsed: readConfig(profile.configPath),
|
|
219
|
+
mcp: canonical.mcp,
|
|
220
|
+
managedAgents: canonical.managedAgents,
|
|
221
|
+
policy: canonical.secretsPolicy,
|
|
222
|
+
ownership: ownedByAgent(loadMcpOwnership(this.homeDir), this.id),
|
|
223
|
+
}).filter((item) => item.action === "create" || item.action === "conflict");
|
|
224
|
+
mismatches.push(...expectedMcp.map((item) => item.description));
|
|
225
|
+
if (!snapshot.instructionsFile?.isSymlink || snapshot.instructionsFile.target !== canonical.instructionsFile)
|
|
226
|
+
mismatches.push("shared instructions are not linked into ~/.zcode/AGENTS.md");
|
|
227
|
+
if (capabilityDeliveryForAgent(this.id, canonical.mcp) === "mcp") {
|
|
228
|
+
const controls = currentSkillControls(readConfig(profile.configPath));
|
|
229
|
+
if (controls.featuresSkill !== false || controls.skillsEnabled !== false)
|
|
230
|
+
mismatches.push("native ZCode Skill discovery is still enabled during Runtime-only delivery");
|
|
231
|
+
}
|
|
232
|
+
return mismatches.length === 0 ? { ok: true } : { ok: false, mismatches };
|
|
233
|
+
}
|
|
234
|
+
}
|