agent-trellis 0.7.0 → 0.8.2
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 +48 -153
- package/dist/adapters/claude-code.js +3 -2
- package/dist/adapters/codex.js +3 -2
- package/dist/adapters/jsonMcp.d.ts +3 -1
- package/dist/adapters/jsonMcp.js +18 -4
- package/dist/adapters/kimi-code.d.ts +21 -0
- package/dist/adapters/kimi-code.js +107 -0
- package/dist/adapters/kiro.js +3 -2
- package/dist/adapters/mcpPlan.d.ts +6 -1
- package/dist/adapters/mcpPlan.js +106 -8
- package/dist/adapters/pi.js +22 -3
- package/dist/adapters/symlinkPlan.d.ts +7 -0
- package/dist/adapters/symlinkPlan.js +16 -7
- package/dist/adapters/zcode.d.ts +21 -0
- package/dist/adapters/zcode.js +234 -0
- package/dist/cli.js +145 -29
- package/dist/commands/doctor.d.ts +7 -2
- package/dist/commands/doctor.js +40 -2
- package/dist/commands/init.d.ts +4 -2
- package/dist/commands/init.js +29 -13
- package/dist/commands/kimi.d.ts +16 -0
- package/dist/commands/kimi.js +64 -0
- package/dist/commands/manage.d.ts +39 -0
- package/dist/commands/manage.js +126 -0
- package/dist/commands/mcp.d.ts +77 -14
- package/dist/commands/mcp.js +169 -29
- package/dist/commands/mcpGateway.d.ts +6 -0
- package/dist/commands/mcpGateway.js +35 -14
- package/dist/commands/mcpImport.d.ts +46 -0
- package/dist/commands/mcpImport.js +359 -0
- package/dist/commands/memory.d.ts +17 -3
- package/dist/commands/memory.js +27 -8
- package/dist/commands/migrate.d.ts +16 -5
- package/dist/commands/migrate.js +93 -17
- package/dist/commands/onboard.d.ts +74 -2
- package/dist/commands/onboard.js +469 -71
- package/dist/commands/rollback.js +28 -2
- package/dist/commands/secretsAudit.js +4 -1
- package/dist/commands/skill.d.ts +52 -6
- package/dist/commands/skill.js +114 -15
- package/dist/commands/sync.js +5 -1
- package/dist/core/adapter.d.ts +11 -1
- package/dist/core/canonical.d.ts +15 -7
- package/dist/core/canonical.js +101 -13
- package/dist/core/capabilitySelection.d.ts +45 -0
- package/dist/core/capabilitySelection.js +124 -0
- package/dist/core/types.d.ts +25 -2
- package/dist/core/types.js +8 -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 +32 -0
- package/dist/lib/backup.js +20 -1
- package/dist/lib/builtinSkills.d.ts +2 -0
- package/dist/lib/builtinSkills.js +4 -0
- package/dist/lib/chatAgents.d.ts +42 -0
- package/dist/lib/chatAgents.js +59 -0
- package/dist/lib/cliMetadata.d.ts +1 -0
- package/dist/lib/cliMetadata.js +14 -0
- package/dist/lib/envVarNames.d.ts +3 -0
- package/dist/lib/envVarNames.js +19 -0
- package/dist/lib/gatewayBackend.d.ts +28 -0
- package/dist/lib/gatewayBackend.js +72 -1
- package/dist/lib/installAgent.d.ts +7 -5
- package/dist/lib/installAgent.js +4 -3
- package/dist/lib/mcpConnect.js +5 -1
- package/dist/lib/mcpMigrateRead.d.ts +2 -0
- package/dist/lib/mcpMigrateRead.js +22 -1
- package/dist/lib/mcpRuntime.d.ts +46 -0
- package/dist/lib/mcpRuntime.js +185 -0
- package/dist/lib/mcpStatusProvider.d.ts +10 -0
- package/dist/lib/mcpStatusProvider.js +25 -0
- package/dist/lib/mcpToolRegistry.d.ts +18 -0
- package/dist/lib/mcpToolRegistry.js +107 -14
- package/dist/lib/memoryGraph.d.ts +1 -1
- package/dist/lib/memoryGraph.js +9 -3
- package/dist/lib/memoryProvider.d.ts +58 -0
- package/dist/lib/memoryProvider.js +175 -0
- package/dist/lib/memoryStatus.d.ts +17 -0
- package/dist/lib/memoryStatus.js +88 -0
- package/dist/lib/nativeMemory.d.ts +19 -0
- package/dist/lib/nativeMemory.js +129 -0
- package/dist/lib/realHomeSnapshot.js +5 -0
- package/dist/lib/runtimeControlProvider.d.ts +16 -0
- package/dist/lib/runtimeControlProvider.js +170 -0
- package/dist/lib/secretEnv.d.ts +2 -1
- package/dist/lib/secretEnv.js +6 -2
- package/dist/lib/skillProvider.d.ts +13 -0
- package/dist/lib/skillProvider.js +210 -0
- package/dist/lib/taskProvider.d.ts +7 -0
- package/dist/lib/taskProvider.js +43 -0
- package/dist/lib/taskStore.d.ts +46 -0
- package/dist/lib/taskStore.js +156 -0
- package/dist/lib/terminalPicker.d.ts +23 -49
- package/dist/lib/terminalPicker.js +80 -218
- package/dist/lib/zcodeOwnership.d.ts +17 -0
- package/dist/lib/zcodeOwnership.js +37 -0
- package/dist/pi-bridge/bundle.js +476 -37
- package/dist/pi-bridge/index.js +42 -21
- package/dist/pi-bridge/schemaTranslate.d.ts +4 -8
- package/dist/pi-bridge/schemaTranslate.js +4 -8
- package/dist/probes/codex.d.ts +7 -0
- package/dist/probes/codex.js +37 -5
- package/dist/probes/kimi-code.d.ts +12 -0
- package/dist/probes/kimi-code.js +55 -0
- package/dist/probes/zcode.d.ts +32 -0
- package/dist/probes/zcode.js +140 -0
- package/dist/sdk.d.ts +1 -1
- package/docs/architecture.md +230 -28
- 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 +496 -42
- package/docs/overview.md +134 -0
- package/docs/overview.zh-CN.md +102 -0
- package/docs/research.md +59 -0
- package/docs/roadmap.md +11 -6
- 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 +6 -2
- package/schema/builtin-skills/trellis-runtime/SKILL.md +117 -0
- package/schema/capability-selection.example.yaml +23 -0
- package/schema/scope.example.yaml +1 -1
- package/schema/servers.example.yaml +15 -1
package/README.md
CHANGED
|
@@ -1,178 +1,73 @@
|
|
|
1
1
|
# Trellis
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/assets/brand-v3/trellis-mark-v3-a.svg" width="88" alt="Trellis logo" />
|
|
5
|
+
</p>
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
defined once, adapted natively into Claude Code, Codex, Kiro, and the [pi coding
|
|
7
|
-
agent](https://github.com/earendil-works/pi), without drift and without a second
|
|
8
|
-
copy of anything.
|
|
7
|
+
<p align="center"><strong>Unified Code Agent Runtime</strong></p>
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
a plaintext secret or a duplicated skill file slip through.
|
|
9
|
+
One canonical source for Skills, MCP, Memory, and shared instructions —
|
|
10
|
+
projected safely into Claude Code, Codex, Kiro, pi, Kimi Code, and ZCode.
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
> 中文:一个 canonical source 统一管理多个 Code Agent 的 Skill、MCP、Memory
|
|
13
|
+
> 与共享指令,支持预览、备份与回滚。
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
least seven open source projects do it (`block/ai-rules`, `ai-rules-sync`,
|
|
18
|
-
`skillshare`, `skills-hub`, `skills-link`, `agent_sync`, and others). None of
|
|
19
|
-
them cover **Kiro** or **pi**, and none of them go past rules/skills into MCP
|
|
20
|
-
servers, shared memory, and secret hygiene as one coherent system. That's the
|
|
21
|
-
gap Trellis fills — see [`docs/research.md`](docs/research.md) for the full
|
|
22
|
-
landscape survey and [`docs/architecture.md`](docs/architecture.md) for what we
|
|
23
|
-
build versus what we deliberately reuse.
|
|
15
|
+

|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
draft rather than inventing a sixth competing standard, and is likely its first
|
|
27
|
-
working implementation.
|
|
28
|
-
|
|
29
|
-
## Install
|
|
17
|
+
## Quick start
|
|
30
18
|
|
|
31
|
-
```
|
|
19
|
+
```bash
|
|
32
20
|
npm install -g agent-trellis
|
|
21
|
+
|
|
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
|
|
33
25
|
```
|
|
34
26
|
|
|
35
|
-
|
|
27
|
+
Something went wrong? `trellis rollback` inverts the last run.
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
walkthrough — example output for each command, what each `migrate`/`sync`
|
|
39
|
-
conflict action means, and troubleshooting.
|
|
29
|
+
## How it compares
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
| | Skills / Rules | MCP | Memory | Secret hygiene | Agents covered |
|
|
32
|
+
|---|---|---|---|---|---|
|
|
33
|
+
| [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 |
|
|
34
|
+
| [skillshare](https://github.com/runkids/skillshare), [skills-hub](https://github.com/qufei1993/skills-hub) | ✅ skills only | ❌ | ❌ | ❌ | Codex, Claude, … |
|
|
35
|
+
| [mcp-router](https://github.com/mcp-router/mcp-router) | ❌ | ✅ desktop router | ❌ | config in its own dashboard | — |
|
|
36
|
+
| [MetaMCP](https://github.com/metatool-ai/metamcp), [mcp-hub](https://github.com/ravitemer/mcp-hub) | ❌ | ✅ aggregator | ❌ | ❌ | — |
|
|
37
|
+
| [cli-agent-orchestrator](https://github.com/awslabs/cli-agent-orchestrator) | ❌ | ❌ | ❌ | ❌ | Runs agent fleets, doesn't unify config |
|
|
38
|
+
| **Trellis** | ✅ | ✅ native config, Runtime, or hub mode | ✅ shared memory + handoffs | ✅ references only, `secrets audit` | Claude Code, Codex, Kiro, pi, Kimi Code, ZCode |
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
Sync tools stop at rules and skills. MCP routers manage servers but hold their
|
|
41
|
+
own config — a second source of truth (we hit real token drift with exactly
|
|
42
|
+
that setup). Trellis is the only one treating Skills + MCP + Memory +
|
|
43
|
+
instructions as **one** canonical system, and the only one covering Kiro and
|
|
44
|
+
pi. Full landscape survey: [`docs/research.md`](docs/research.md).
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
this machine, then resolves two independent choices: a **migration source**
|
|
49
|
-
(read from — auto-selected if only one present agent has real content,
|
|
50
|
-
prompted with a numbered choice if more than one, skippable if starting
|
|
51
|
-
fresh; pass `--agent <id>` to skip the prompt) and a **managed set** (written
|
|
52
|
-
to — zero or more agents, chosen explicitly; pass `--manage <ids>` or
|
|
53
|
-
`--manage none` to skip the prompt). The source is **not** managed by
|
|
54
|
-
default — importing from Claude Code doesn't mean Trellis starts writing to
|
|
55
|
-
Claude Code too, unless you explicitly include it. Selecting an agent that
|
|
56
|
-
isn't installed yet (e.g. pi) is itself the authorization to install it —
|
|
57
|
-
one confirmation, then a real `npm install -g <package>`; Kiro has no CLI
|
|
58
|
-
package and is refused with its download URL instead. Once resolved, it
|
|
59
|
-
runs `migrate`, `sync`, `mcp sync`, and `secrets audit` against the managed
|
|
60
|
-
set — the whole onboarding path in one command, no follow-up commands to
|
|
61
|
-
type by hand. If no agent is detected, it prints each supported agent's
|
|
62
|
-
real install command/URL and stops — it never installs anything on its own
|
|
63
|
-
initiative. Add `--dry-run` to preview the whole thing with zero writes.
|
|
46
|
+
## Design principles
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
1. **One canonical source, many adapters** — agent-native configs are
|
|
49
|
+
generated artifacts, never hand-edited.
|
|
50
|
+
2. **No plaintext secrets, ever** — outputs hold variable references;
|
|
51
|
+
`trellis secrets audit` rejects raw values.
|
|
52
|
+
3. **Verify, don't assume** — every adapter claim is backed by an executed
|
|
53
|
+
probe in isolated Docker sandboxes, never a developer's own dotfiles.
|
|
68
54
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
**Already using Claude Code, Codex, Kiro, or pi and want to migrate what you
|
|
73
|
-
already have?**
|
|
74
|
-
|
|
75
|
-
1. `trellis init` — creates `~/.trellis/` with a minimal skeleton (only what's
|
|
76
|
-
missing; never overwrites a file you already have) and tells you which of
|
|
77
|
-
the four agents it found on this machine.
|
|
78
|
-
2. `trellis migrate --from <agent>` — once per agent you already use. Copies
|
|
79
|
-
that agent's real skills and instructions into canonical source. Never
|
|
80
|
-
overwrites: an already-identical skill is reported and skipped, a genuine
|
|
81
|
-
conflict is reported and left for you to resolve by hand. Add `--dry-run`
|
|
82
|
-
to preview first.
|
|
83
|
-
3. `trellis sync` — distributes canonical skills/instructions to every
|
|
84
|
-
**managed** agent (`~/.trellis/managed.yaml` — empty by default; edit it
|
|
85
|
-
by hand or let `trellis onboard` write it). An agent that's merely present
|
|
86
|
-
but not managed is never touched. Add `--dry-run` to preview first.
|
|
87
|
-
4. `trellis mcp sync` — distributes `~/.trellis/mcp/servers.yaml` (see
|
|
88
|
-
[`schema/servers.example.yaml`](schema/servers.example.yaml)) to every
|
|
89
|
-
managed agent's native MCP config (create/repair only — see Known
|
|
90
|
-
limitations).
|
|
91
|
-
5. `trellis secrets audit` — fails non-zero if any managed agent's real
|
|
92
|
-
config holds a literal credential or an unexpected env var name.
|
|
93
|
-
6. `trellis doctor` — read-only scan of every present agent's current state
|
|
94
|
-
(managed or not); run any time to check for drift.
|
|
95
|
-
|
|
96
|
-
**Starting from nothing?** Skip step 2 — `trellis init`'s placeholder
|
|
97
|
-
`agents.md` and empty `skills/` are a fine starting point; edit them by hand.
|
|
98
|
-
|
|
99
|
-
**Every real write `sync`/`mcp sync`/`onboard` perform is backed up
|
|
100
|
-
first, automatically — no flag to opt in.** Before creating, repairing,
|
|
101
|
-
or removing anything, each is recorded to a structured, timestamped run
|
|
102
|
-
under `~/.trellis/backups/`, with enough information to invert it exactly.
|
|
103
|
-
`trellis rollback` undoes one recorded run — omit a run id for the most
|
|
104
|
-
recent one:
|
|
55
|
+
Trellis aligns with the emerging [`.agents Protocol`](https://dotagentsprotocol.com)
|
|
56
|
+
draft rather than inventing another standard.
|
|
105
57
|
|
|
106
|
-
|
|
107
|
-
$ trellis rollback
|
|
108
|
-
rollback 2026-09-13T04-52-18-727Z-mcp-sync
|
|
109
|
-
✅ 3 restored, 0 conflict(s), 0 already reverted
|
|
110
|
-
- [restore] restore /Users/you/.claude.json to its content before this run (...)
|
|
111
|
-
```
|
|
58
|
+
## Docs
|
|
112
59
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
restore with zero writes.
|
|
60
|
+
- [Overview](docs/overview.md) ([中文](docs/overview.zh-CN.md)) — what it is and why
|
|
61
|
+
- [Getting started](docs/getting-started.md) — every command, with example output
|
|
62
|
+
- [Architecture](docs/architecture.md) — what we build vs. deliberately reuse
|
|
63
|
+
- [Research](docs/research.md) — the full competitive landscape
|
|
118
64
|
|
|
119
65
|
## Status
|
|
120
66
|
|
|
121
|
-
**Early, pre-1.0.** All
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
[`docs/architecture.md`](docs/architecture.md)'s testing philosophy). See
|
|
126
|
-
[`docs/roadmap.md`](docs/roadmap.md) for what's shipped (P0–P6) vs. planned
|
|
127
|
-
(P7, a GUI). `onboard` picks one agent as the migration base when more than
|
|
128
|
-
one is present — merging differing content across multiple agents into one
|
|
129
|
-
result is named future work, not built yet.
|
|
130
|
-
|
|
131
|
-
**Known limitations, honestly stated rather than discovered the hard way:**
|
|
132
|
-
- MCP servers are never spawned/handshake-tested by `trellis mcp sync` or
|
|
133
|
-
`trellis sync` — only that the *config* is written correctly.
|
|
134
|
-
`trellis doctor --probe-mcp` is the one command that actually connects,
|
|
135
|
-
and it's opt-in.
|
|
136
|
-
- The pi bridge extension (`trellis-pi-mcp-bridge`) has been verified to
|
|
137
|
-
load and register tools without erroring, never against a real LLM tool
|
|
138
|
-
call in production.
|
|
139
|
-
- Verification has run against real Docker containers and a real,
|
|
140
|
-
isolated pi CLI install — not yet against a developer's actual, existing
|
|
141
|
-
`~/.claude`/`~/.codex`/`~/.kiro`/`~/.pi` in daily use. If you hit
|
|
142
|
-
something a clean-room sandbox wouldn't have caught, please open an
|
|
143
|
-
issue.
|
|
144
|
-
- Automatic removal of an MCP server is deliberately unsupported (create/
|
|
145
|
-
repair only) until an ownership-tracking mechanism exists — see
|
|
146
|
-
`docs/roadmap.md`'s P2 note.
|
|
147
|
-
- `~/.trellis/backups/` has no automatic pruning yet — every `sync`/
|
|
148
|
-
`mcp sync`/`onboard` run that performs a real write adds one more run
|
|
149
|
-
directory, with no cap. Delete old ones by hand for now.
|
|
150
|
-
|
|
151
|
-
A canonical MCP server can also declare `enabled: false` (kept defined,
|
|
152
|
-
never written to any agent) and `static_env` (a value that was never a
|
|
153
|
-
secret — an email, an environment tag — written verbatim instead of
|
|
154
|
-
resolved by name; still scanned against `reject_patterns` like every
|
|
155
|
-
other literal field). Before ever writing a name-only `env` entry,
|
|
156
|
-
`mcp sync` now checks it actually resolves through the same source
|
|
157
|
-
`secrets audit` and the pi bridge use — an unresolvable name is refused
|
|
158
|
-
as a conflict, not silently written and left to break that server's
|
|
159
|
-
connection once the agent starts it. Both found via real-machine
|
|
160
|
-
dogfooding, not a synthetic fixture — see
|
|
161
|
-
[`schema/servers.example.yaml`](schema/servers.example.yaml).
|
|
162
|
-
|
|
163
|
-
## Design principles
|
|
164
|
-
|
|
165
|
-
1. **One canonical source, many adapters.** Each agent's native config file is
|
|
166
|
-
a generated or symlinked artifact, never hand-edited.
|
|
167
|
-
2. **Reuse before building.** MCP aggregation, memory, and secret-reference
|
|
168
|
-
patterns already have mature open source answers — Trellis wires to them
|
|
169
|
-
instead of reimplementing them. See [`docs/architecture.md`](docs/architecture.md).
|
|
170
|
-
3. **No plaintext secrets, ever.** Every adapter output holds variable
|
|
171
|
-
references only; `trellis secrets audit` scans for and rejects raw values.
|
|
172
|
-
4. **Verify, don't assume.** Every claim this project makes about an agent's
|
|
173
|
-
behavior (file format, discovery path, MCP support) is backed by an
|
|
174
|
-
executed probe, not documentation-reading. Adapters that can't be verified
|
|
175
|
-
this way don't ship.
|
|
67
|
+
**Early, pre-1.0.** All CLI commands are implemented, unit-tested, and verified
|
|
68
|
+
against real agents in isolated containers. Known limitations are listed in
|
|
69
|
+
[Getting started](docs/getting-started.md); please open an issue for anything
|
|
70
|
+
a clean-room sandbox wouldn't catch.
|
|
176
71
|
|
|
177
72
|
## License
|
|
178
73
|
|
|
@@ -13,6 +13,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { isInScope } from "../core/adapter.js";
|
|
16
|
+
import { usesNativeCapabilityDelivery } from "../core/types.js";
|
|
16
17
|
import * as claudeCodeProbe from "../probes/claude-code.js";
|
|
17
18
|
import { applySymlinkPlan, planSymlinks } from "./symlinkPlan.js";
|
|
18
19
|
import { applyJsonMcp, planJsonMcp, renderJsonServerEntry } from "./jsonMcp.js";
|
|
@@ -32,7 +33,7 @@ export class ClaudeCodeAdapter {
|
|
|
32
33
|
const canonicalRoot = dirname(canonical.instructionsFile);
|
|
33
34
|
const skillsRoot = join(this.homeDir, ".claude", "skills");
|
|
34
35
|
const desiredSkills = canonical.skills
|
|
35
|
-
.filter((skill) => isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
36
|
+
.filter((skill) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
36
37
|
.map((skill) => ({ name: skill.name, target: skill.dir }));
|
|
37
38
|
const skillItems = planSymlinks({
|
|
38
39
|
rootDir: skillsRoot,
|
|
@@ -82,7 +83,7 @@ export class ClaudeCodeAdapter {
|
|
|
82
83
|
return { ok: false, mismatches: ["claude-code is not present on this machine"] };
|
|
83
84
|
}
|
|
84
85
|
const mismatches = [];
|
|
85
|
-
const desiredNames = new Set(canonical.skills.filter((s) => isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
86
|
+
const desiredNames = new Set(canonical.skills.filter((s) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
86
87
|
const actualSkills = new Set(snapshot.skillRoots.flatMap((root) => root.skills).map((s) => s.name));
|
|
87
88
|
for (const name of desiredNames) {
|
|
88
89
|
if (!actualSkills.has(name)) {
|
package/dist/adapters/codex.js
CHANGED
|
@@ -19,6 +19,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
19
19
|
import { basename, dirname, join } from "node:path";
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { isInScope } from "../core/adapter.js";
|
|
22
|
+
import { usesNativeCapabilityDelivery } from "../core/types.js";
|
|
22
23
|
import * as codexProbe from "../probes/codex.js";
|
|
23
24
|
import { readInstructionsPath } from "../probes/codex.js";
|
|
24
25
|
import { applySymlinkPlan, planSymlinks } from "./symlinkPlan.js";
|
|
@@ -40,7 +41,7 @@ export class CodexAdapter {
|
|
|
40
41
|
const canonicalRoot = dirname(canonical.instructionsFile);
|
|
41
42
|
const skillsRoot = join(this.homeDir, ".agents", "skills");
|
|
42
43
|
const desiredSkills = canonical.skills
|
|
43
|
-
.filter((skill) => isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
44
|
+
.filter((skill) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
44
45
|
.map((skill) => ({ name: skill.name, target: skill.dir }));
|
|
45
46
|
const skillItems = planSymlinks({
|
|
46
47
|
rootDir: skillsRoot,
|
|
@@ -136,7 +137,7 @@ export class CodexAdapter {
|
|
|
136
137
|
return { ok: false, mismatches: ["codex is not present on this machine"] };
|
|
137
138
|
}
|
|
138
139
|
const mismatches = [];
|
|
139
|
-
const desiredNames = new Set(canonical.skills.filter((s) => isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
140
|
+
const desiredNames = new Set(canonical.skills.filter((s) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
140
141
|
const actualSkills = new Set(snapshot.skillRoots.flatMap((root) => root.skills).map((s) => s.name));
|
|
141
142
|
for (const name of desiredNames) {
|
|
142
143
|
if (!actualSkills.has(name)) {
|
|
@@ -15,6 +15,7 @@ import type { AdapterPlanItem } from "../core/adapter.js";
|
|
|
15
15
|
* Codex's TOML does — see tomlSection.ts's two-table rendering)
|
|
16
16
|
* (trellis-mcp-static-env-and-disabled-servers design.md D4). */
|
|
17
17
|
export declare function renderJsonServerEntry(def: McpServerDef): Record<string, unknown>;
|
|
18
|
+
export type JsonMcpEntryRenderer = (def: McpServerDef, name: string) => Record<string, unknown>;
|
|
18
19
|
export declare function planJsonMcp(opts: {
|
|
19
20
|
configPath: string;
|
|
20
21
|
parsed: Record<string, unknown> | undefined;
|
|
@@ -27,9 +28,10 @@ export declare function planJsonMcp(opts: {
|
|
|
27
28
|
* pass `{}`) to get today's create/repair/conflict-only behavior with
|
|
28
29
|
* zero removal candidates. */
|
|
29
30
|
ownership?: Record<string, unknown>;
|
|
31
|
+
render?: JsonMcpEntryRenderer;
|
|
30
32
|
}): AdapterPlanItem[];
|
|
31
33
|
/** Merges every `"create"` MCP item into `parsed` (or a fresh `{}` if the
|
|
32
34
|
* file didn't exist) and deletes every `"remove"` item's key, returning
|
|
33
35
|
* the object to stringify — every sibling top-level key on `parsed` is
|
|
34
36
|
* spread through untouched. */
|
|
35
|
-
export declare function applyJsonMcp(parsed: Record<string, unknown> | undefined, items: AdapterPlanItem[]): Record<string, unknown>;
|
|
37
|
+
export declare function applyJsonMcp(parsed: Record<string, unknown> | undefined, items: AdapterPlanItem[], render?: JsonMcpEntryRenderer): Record<string, unknown>;
|
package/dist/adapters/jsonMcp.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { deepEqual } from "../lib/deepEqual.js";
|
|
10
10
|
import { resolvedEnvTextMap } from "../lib/mcpMigrateRead.js";
|
|
11
|
-
import { resolveMcpPlan } from "./mcpPlan.js";
|
|
11
|
+
import { LEGACY_GATEWAY_ENTRY_NAME, LEGACY_RUNTIME_ENTRY_NAME, resolveMcpPlan } from "./mcpPlan.js";
|
|
12
12
|
/** Renders the native JSON shape Claude Code/Kiro's `mcpServers` map
|
|
13
13
|
* expects — `env` names render as `${VAR}` references, `staticEnv`
|
|
14
14
|
* entries render as their literal values, both in the same map (JSON's
|
|
@@ -32,11 +32,12 @@ export function renderJsonServerEntry(def) {
|
|
|
32
32
|
}
|
|
33
33
|
export function planJsonMcp(opts) {
|
|
34
34
|
const { configPath, parsed, mcp, agentId, managedAgents, policy, ownership } = opts;
|
|
35
|
+
const render = opts.render ?? ((def, _name) => renderJsonServerEntry(def));
|
|
35
36
|
const { desired, conflicts } = resolveMcpPlan(agentId, mcp, managedAgents, policy);
|
|
36
37
|
const existingServers = parsed?.mcpServers ?? {};
|
|
37
38
|
const items = [];
|
|
38
39
|
for (const { name, def } of desired) {
|
|
39
|
-
const rendered =
|
|
40
|
+
const rendered = render(def, name);
|
|
40
41
|
const current = existingServers[name];
|
|
41
42
|
if (deepEqual(current, rendered)) {
|
|
42
43
|
continue; // already correct — no-op
|
|
@@ -59,6 +60,19 @@ export function planJsonMcp(opts) {
|
|
|
59
60
|
for (const [name, expected] of Object.entries(ownership ?? {})) {
|
|
60
61
|
if (desiredNames.has(name))
|
|
61
62
|
continue; // still wanted — not a removal candidate at all
|
|
63
|
+
if ((name === LEGACY_GATEWAY_ENTRY_NAME || name === LEGACY_RUNTIME_ENTRY_NAME) && desiredNames.has("trellis")) {
|
|
64
|
+
const current = existingServers[name];
|
|
65
|
+
if (current !== undefined && deepEqual(current, expected)) {
|
|
66
|
+
items.push({
|
|
67
|
+
action: "remove",
|
|
68
|
+
kind: "mcp",
|
|
69
|
+
target: configPath,
|
|
70
|
+
mcpRemove: { name },
|
|
71
|
+
description: `legacy MCP server "${name}" removed from ${configPath} after Trellis Runtime key migration`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
62
76
|
const current = existingServers[name];
|
|
63
77
|
if (current === undefined)
|
|
64
78
|
continue; // already gone — nothing to remove, ledger is pruned separately
|
|
@@ -78,7 +92,7 @@ export function planJsonMcp(opts) {
|
|
|
78
92
|
* file didn't exist) and deletes every `"remove"` item's key, returning
|
|
79
93
|
* the object to stringify — every sibling top-level key on `parsed` is
|
|
80
94
|
* spread through untouched. */
|
|
81
|
-
export function applyJsonMcp(parsed, items) {
|
|
95
|
+
export function applyJsonMcp(parsed, items, render = (def, _name) => renderJsonServerEntry(def)) {
|
|
82
96
|
const base = parsed ?? {};
|
|
83
97
|
const existingServers = base.mcpServers ?? {};
|
|
84
98
|
const mergedServers = { ...existingServers };
|
|
@@ -86,7 +100,7 @@ export function applyJsonMcp(parsed, items) {
|
|
|
86
100
|
if (item.kind !== "mcp")
|
|
87
101
|
continue;
|
|
88
102
|
if (item.action === "create" && item.mcpWrite) {
|
|
89
|
-
mergedServers[item.mcpWrite.name] =
|
|
103
|
+
mergedServers[item.mcpWrite.name] = render(item.mcpWrite.def, item.mcpWrite.name);
|
|
90
104
|
}
|
|
91
105
|
else if (item.action === "remove" && item.mcpRemove) {
|
|
92
106
|
delete mergedServers[item.mcpRemove.name];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code Runtime-first adapter.
|
|
3
|
+
*
|
|
4
|
+
* Runtime delivery owns only ~/.kimi-code/mcp.json. Native Kimi Skills and
|
|
5
|
+
* AGENTS.md remain opt-in for `native`/`both`; Runtime-only delivery uses
|
|
6
|
+
* `trellis kimi`, which launches Kimi with an empty --skills-dir root so the
|
|
7
|
+
* shared ~/.agents/skills tree is not discovered a second time.
|
|
8
|
+
*/
|
|
9
|
+
import type { AdapterPlanItem, AdapterProbeResult, AdapterVerifyResult, TrellisAdapter } from "../core/adapter.js";
|
|
10
|
+
import type { CanonicalSource } from "../core/types.js";
|
|
11
|
+
import type { BackupSession } from "../lib/backup.js";
|
|
12
|
+
export declare class KimiCodeAdapter implements TrellisAdapter {
|
|
13
|
+
private readonly homeDir;
|
|
14
|
+
readonly name = "Kimi Code";
|
|
15
|
+
readonly id: "kimi-code";
|
|
16
|
+
constructor(homeDir?: string);
|
|
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,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code Runtime-first adapter.
|
|
3
|
+
*
|
|
4
|
+
* Runtime delivery owns only ~/.kimi-code/mcp.json. Native Kimi Skills and
|
|
5
|
+
* AGENTS.md remain opt-in for `native`/`both`; Runtime-only delivery uses
|
|
6
|
+
* `trellis kimi`, which launches Kimi with an empty --skills-dir root so the
|
|
7
|
+
* shared ~/.agents/skills tree is not discovered a second time.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { isInScope } from "../core/adapter.js";
|
|
13
|
+
import { usesNativeCapabilityDelivery } from "../core/types.js";
|
|
14
|
+
import * as kimiProbe from "../probes/kimi-code.js";
|
|
15
|
+
import { applyJsonMcp, planJsonMcp, renderJsonServerEntry } from "./jsonMcp.js";
|
|
16
|
+
import { RUNTIME_COMMAND, RUNTIME_ENTRY_NAME } from "./mcpPlan.js";
|
|
17
|
+
import { applySymlinkPlan, planSymlinks } from "./symlinkPlan.js";
|
|
18
|
+
import { loadMcpOwnership, ownedByAgent, recordOwned, forgetOwned, saveMcpOwnership } from "../lib/mcpOwnership.js";
|
|
19
|
+
const KIMI_RUNTIME_STARTUP_TIMEOUT_MS = 30_000;
|
|
20
|
+
function renderKimiServerEntry(def, name) {
|
|
21
|
+
const entry = renderJsonServerEntry(def);
|
|
22
|
+
if (def.transport === "stdio")
|
|
23
|
+
delete entry.type;
|
|
24
|
+
if (name === RUNTIME_ENTRY_NAME && def.command === RUNTIME_COMMAND) {
|
|
25
|
+
entry.deferred = true;
|
|
26
|
+
entry.startupTimeoutMs = KIMI_RUNTIME_STARTUP_TIMEOUT_MS;
|
|
27
|
+
}
|
|
28
|
+
return entry;
|
|
29
|
+
}
|
|
30
|
+
export class KimiCodeAdapter {
|
|
31
|
+
homeDir;
|
|
32
|
+
name = "Kimi Code";
|
|
33
|
+
id = "kimi-code";
|
|
34
|
+
constructor(homeDir = homedir()) {
|
|
35
|
+
this.homeDir = homeDir;
|
|
36
|
+
}
|
|
37
|
+
async probe() {
|
|
38
|
+
const snapshot = await kimiProbe.probe(this.homeDir);
|
|
39
|
+
return { present: snapshot.present, version: snapshot.version };
|
|
40
|
+
}
|
|
41
|
+
async plan(canonical) {
|
|
42
|
+
const native = usesNativeCapabilityDelivery(this.id, canonical.mcp);
|
|
43
|
+
const items = [];
|
|
44
|
+
const root = join(this.homeDir, ".kimi-code");
|
|
45
|
+
const canonicalRoot = dirname(canonical.instructionsFile);
|
|
46
|
+
items.push(...planSymlinks({
|
|
47
|
+
rootDir: join(root, "skills"),
|
|
48
|
+
desired: native
|
|
49
|
+
? canonical.skills
|
|
50
|
+
.filter((skill) => isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
51
|
+
.map((skill) => ({ name: skill.name, target: skill.dir }))
|
|
52
|
+
: [],
|
|
53
|
+
canonicalRoot: join(canonicalRoot, "skills"),
|
|
54
|
+
kind: "skill",
|
|
55
|
+
}));
|
|
56
|
+
items.push(...planSymlinks({
|
|
57
|
+
rootDir: root,
|
|
58
|
+
desired: native ? [{ name: "AGENTS.md", target: canonical.instructionsFile }] : [],
|
|
59
|
+
canonicalRoot,
|
|
60
|
+
kind: "instructions",
|
|
61
|
+
}));
|
|
62
|
+
const configPath = join(this.homeDir, ".kimi-code", "mcp.json");
|
|
63
|
+
const parsed = existsSync(configPath) ? JSON.parse(readFileSync(configPath, "utf-8")) : undefined;
|
|
64
|
+
const ownership = ownedByAgent(loadMcpOwnership(this.homeDir), this.id);
|
|
65
|
+
items.push(...planJsonMcp({
|
|
66
|
+
configPath,
|
|
67
|
+
parsed,
|
|
68
|
+
mcp: canonical.mcp,
|
|
69
|
+
agentId: this.id,
|
|
70
|
+
managedAgents: canonical.managedAgents,
|
|
71
|
+
policy: canonical.secretsPolicy,
|
|
72
|
+
ownership,
|
|
73
|
+
render: renderKimiServerEntry,
|
|
74
|
+
}));
|
|
75
|
+
return items;
|
|
76
|
+
}
|
|
77
|
+
async apply(plan, backup) {
|
|
78
|
+
await applySymlinkPlan(plan.filter((item) => item.kind === "skill" || item.kind === "instructions"), backup);
|
|
79
|
+
const mcpWrites = plan.filter((item) => item.kind === "mcp" && (item.action === "create" || item.action === "remove"));
|
|
80
|
+
if (mcpWrites.length === 0)
|
|
81
|
+
return;
|
|
82
|
+
const configPath = mcpWrites[0].target;
|
|
83
|
+
const parsed = existsSync(configPath) ? JSON.parse(readFileSync(configPath, "utf-8")) : undefined;
|
|
84
|
+
const merged = applyJsonMcp(parsed, mcpWrites, renderKimiServerEntry);
|
|
85
|
+
backup.writeFile(configPath, `${JSON.stringify(merged, null, 2)}\n`);
|
|
86
|
+
let ownership = loadMcpOwnership(this.homeDir);
|
|
87
|
+
for (const item of mcpWrites) {
|
|
88
|
+
if (item.action === "create" && item.mcpWrite)
|
|
89
|
+
ownership = recordOwned(ownership, this.id, item.mcpWrite.name, renderKimiServerEntry(item.mcpWrite.def, item.mcpWrite.name));
|
|
90
|
+
if (item.action === "remove" && item.mcpRemove)
|
|
91
|
+
ownership = forgetOwned(ownership, this.id, item.mcpRemove.name);
|
|
92
|
+
}
|
|
93
|
+
saveMcpOwnership(this.homeDir, ownership);
|
|
94
|
+
}
|
|
95
|
+
async verify(canonical) {
|
|
96
|
+
const snapshot = await kimiProbe.probe(this.homeDir);
|
|
97
|
+
if (!snapshot.present)
|
|
98
|
+
return { ok: false, mismatches: ["kimi-code is not present on this machine"] };
|
|
99
|
+
const desired = new Set(canonical.skills.filter((skill) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, skill.scope, canonical.managedAgents)).map((skill) => skill.name));
|
|
100
|
+
const actual = new Set(snapshot.skillRoots.flatMap((root) => root.skills).filter((skill) => skill.dir.includes("/.kimi-code/skills/")).map((skill) => skill.name));
|
|
101
|
+
const mismatches = [
|
|
102
|
+
...[...desired].filter((name) => !actual.has(name)).map((name) => `skill "${name}" is missing on Kimi Code`),
|
|
103
|
+
...[...actual].filter((name) => !desired.has(name)).map((name) => `skill "${name}" is not in Kimi Code's canonical scope`),
|
|
104
|
+
];
|
|
105
|
+
return mismatches.length === 0 ? { ok: true } : { ok: false, mismatches };
|
|
106
|
+
}
|
|
107
|
+
}
|
package/dist/adapters/kiro.js
CHANGED
|
@@ -19,6 +19,7 @@ import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
19
19
|
import { dirname, join } from "node:path";
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { isInScope } from "../core/adapter.js";
|
|
22
|
+
import { usesNativeCapabilityDelivery } from "../core/types.js";
|
|
22
23
|
import * as kiroProbe from "../probes/kiro.js";
|
|
23
24
|
import { applySymlinkPlan, planSymlinks } from "./symlinkPlan.js";
|
|
24
25
|
import { applyJsonMcp, planJsonMcp, renderJsonServerEntry } from "./jsonMcp.js";
|
|
@@ -48,7 +49,7 @@ export class KiroAdapter {
|
|
|
48
49
|
const canonicalRoot = dirname(canonical.instructionsFile);
|
|
49
50
|
const skillsRoot = join(this.homeDir, ".kiro", "skills");
|
|
50
51
|
const desiredSkills = canonical.skills
|
|
51
|
-
.filter((skill) => isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
52
|
+
.filter((skill) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, skill.scope, canonical.managedAgents))
|
|
52
53
|
.map((skill) => ({ name: skill.name, target: skill.dir }));
|
|
53
54
|
const skillItems = planSymlinks({
|
|
54
55
|
rootDir: skillsRoot,
|
|
@@ -161,7 +162,7 @@ export class KiroAdapter {
|
|
|
161
162
|
return { ok: false, mismatches: ["kiro is not present on this machine"] };
|
|
162
163
|
}
|
|
163
164
|
const mismatches = [];
|
|
164
|
-
const desiredNames = new Set(canonical.skills.filter((s) => isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
165
|
+
const desiredNames = new Set(canonical.skills.filter((s) => usesNativeCapabilityDelivery(this.id, canonical.mcp) && isInScope(this.id, s.scope, canonical.managedAgents)).map((s) => s.name));
|
|
165
166
|
const actualSkills = new Set(snapshot.skillRoots.flatMap((root) => root.skills).map((s) => s.name));
|
|
166
167
|
for (const name of desiredNames) {
|
|
167
168
|
if (!actualSkills.has(name)) {
|
|
@@ -18,12 +18,17 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { AgentId, McpConfig, McpServerDef, SecretsPolicy } from "../core/types.js";
|
|
20
20
|
export declare const HUB_ENTRY_NAME = "trellis-hub";
|
|
21
|
-
export declare const GATEWAY_ENTRY_NAME = "trellis
|
|
21
|
+
export declare const GATEWAY_ENTRY_NAME = "trellis";
|
|
22
|
+
export declare const LEGACY_GATEWAY_ENTRY_NAME = "trellis-gateway";
|
|
23
|
+
export declare const RUNTIME_ENTRY_NAME = "trellis";
|
|
24
|
+
export declare const LEGACY_RUNTIME_ENTRY_NAME = "trellis-runtime";
|
|
22
25
|
/** The command an agent spawns in gateway mode. Bare `trellis` rather than
|
|
23
26
|
* an absolute path: the entry has to keep working across reinstalls and
|
|
24
27
|
* version bumps, and every agent resolves it through the same PATH the
|
|
25
28
|
* user installed the CLI onto. */
|
|
26
29
|
export declare const GATEWAY_COMMAND = "trellis";
|
|
30
|
+
export declare const RUNTIME_COMMAND = "trellis";
|
|
31
|
+
export declare function isOAuthMcpServer(def: McpServerDef): boolean;
|
|
27
32
|
/**
|
|
28
33
|
* Gateway mode applies to every managed agent when `agents` is omitted —
|
|
29
34
|
* the expected normal case (design.md D14). An explicit list narrows it,
|