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/dist/cli.js
CHANGED
|
@@ -16,12 +16,13 @@ import { runMcpAuth } from "./commands/mcpAuth.js";
|
|
|
16
16
|
import { runSecretsAudit } from "./commands/secretsAudit.js";
|
|
17
17
|
import { runRollback } from "./commands/rollback.js";
|
|
18
18
|
import { runSkillList, runSkillAdd, runSkillRemove, runSkillUpdateBuiltin } from "./commands/skill.js";
|
|
19
|
-
import {
|
|
19
|
+
import { runRemoteSkillAdd, runRemoteSkillList, runRemoteSkillUpdate } from "./commands/remoteSkill.js";
|
|
20
|
+
import { collectMemoryList, runMemoryExtraction, runMemorySync } from "./commands/memory.js";
|
|
20
21
|
import { parseManageArgs, runManage } from "./commands/manage.js";
|
|
21
22
|
import { runKimi } from "./commands/kimi.js";
|
|
22
23
|
import { TRELLIS_VERSION } from "./lib/cliMetadata.js";
|
|
23
24
|
import { parseSyncArgs } from "./lib/syncArgs.js";
|
|
24
|
-
const KNOWN_COMMANDS = ["onboard", "init", "migrate", "doctor", "sync", "mcp", "mcp-gateway", "mcp-runtime", "skill", "memory", "manage", "kimi", "secrets", "rollback"];
|
|
25
|
+
const KNOWN_COMMANDS = ["onboard", "init", "migrate", "doctor", "sync", "mcp", "mcp-gateway", "mcp-runtime", "skill", "add", "update", "memory", "manage", "kimi", "secrets", "rollback"];
|
|
25
26
|
function printUsage() {
|
|
26
27
|
console.log(`trellis - a single source of capability for every coding agent
|
|
27
28
|
|
|
@@ -90,6 +91,22 @@ Commands:
|
|
|
90
91
|
Distribute skills/instructions to each agent (omit target for both)
|
|
91
92
|
--dry-run preview the plan, write nothing
|
|
92
93
|
--json machine-readable output, no report text
|
|
94
|
+
add <github-source> --skill <name>
|
|
95
|
+
Import one GitHub-hosted Skill into ~/.trellis/skills/, record
|
|
96
|
+
immutable provenance, then sync it only to managed Agents.
|
|
97
|
+
--skill, -s <name> required unless --list
|
|
98
|
+
--branch <ref> Git branch, tag, or ref; default is
|
|
99
|
+
the source repository's default branch
|
|
100
|
+
--agent, -a <ids|*> restrict delivery to managed Agent ids
|
|
101
|
+
--list show discovered Skills without import
|
|
102
|
+
--global, --yes accepted skills-CLI compatibility flags
|
|
103
|
+
--dry-run fetch and preview; write nothing
|
|
104
|
+
--json machine-readable output
|
|
105
|
+
update [skills...]
|
|
106
|
+
Safely refresh remotely tracked Skills. Refuses to overwrite a
|
|
107
|
+
canonical Skill edited since its recorded import.
|
|
108
|
+
--dry-run preview writes only
|
|
109
|
+
--json machine-readable output
|
|
93
110
|
mcp sync Distribute MCP servers to each agent's native config
|
|
94
111
|
(create/repair, plus ownership-ledger-gated removal — an
|
|
95
112
|
entry is only ever removed when it's still exactly what
|
|
@@ -130,8 +147,9 @@ Commands:
|
|
|
130
147
|
upstream capabilities; existing mcp-gateway configs remain valid
|
|
131
148
|
as a compatibility alias
|
|
132
149
|
mcp remove <name>
|
|
133
|
-
Remove a canonical MCP server
|
|
134
|
-
|
|
150
|
+
Remove a canonical MCP server, then run the same sync as
|
|
151
|
+
mcp sync so every managed agent's Trellis-owned entry for
|
|
152
|
+
it is removed too
|
|
135
153
|
--dry-run preview the plan, write nothing
|
|
136
154
|
--json machine-readable output, no report text
|
|
137
155
|
skill list
|
|
@@ -202,6 +220,31 @@ See docs/roadmap.md for what's built vs. planned.`);
|
|
|
202
220
|
function printVersion() {
|
|
203
221
|
console.log(TRELLIS_VERSION);
|
|
204
222
|
}
|
|
223
|
+
function remoteOptionValues(args, flags) {
|
|
224
|
+
const values = [];
|
|
225
|
+
const positions = new Set();
|
|
226
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
227
|
+
const arg = args[index];
|
|
228
|
+
const matching = flags.find((flag) => arg === flag || arg.startsWith(`${flag}=`));
|
|
229
|
+
if (!matching)
|
|
230
|
+
continue;
|
|
231
|
+
positions.add(index);
|
|
232
|
+
if (arg.startsWith(`${matching}=`)) {
|
|
233
|
+
values.push(arg.slice(matching.length + 1));
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const value = args[index + 1];
|
|
237
|
+
if (!value || value.startsWith("-"))
|
|
238
|
+
return { values, positions, error: `${matching} needs a value` };
|
|
239
|
+
positions.add(index + 1);
|
|
240
|
+
values.push(value);
|
|
241
|
+
index += 1;
|
|
242
|
+
}
|
|
243
|
+
return { values, positions };
|
|
244
|
+
}
|
|
245
|
+
function remotePositionals(args, consumed) {
|
|
246
|
+
return args.filter((arg, index) => !consumed.has(index) && !arg.startsWith("-"));
|
|
247
|
+
}
|
|
205
248
|
async function main(argv) {
|
|
206
249
|
const [command, ...rest] = argv;
|
|
207
250
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -310,6 +353,57 @@ async function main(argv) {
|
|
|
310
353
|
process.exitCode = exitCode;
|
|
311
354
|
return;
|
|
312
355
|
}
|
|
356
|
+
if (command === "add") {
|
|
357
|
+
const skill = remoteOptionValues(rest, ["--skill", "-s"]);
|
|
358
|
+
const branch = remoteOptionValues(rest, ["--branch"]);
|
|
359
|
+
const agents = remoteOptionValues(rest, ["--agent", "-a"]);
|
|
360
|
+
const consumed = new Set([...skill.positions, ...branch.positions, ...agents.positions]);
|
|
361
|
+
const positionals = remotePositionals(rest, consumed);
|
|
362
|
+
const json = rest.includes("--json");
|
|
363
|
+
const prohibited = rest.find((arg) => /^(--copy|--project|--local)(?:=|$)/.test(arg));
|
|
364
|
+
if (prohibited) {
|
|
365
|
+
const detail = "Trellis remote Skills are always imported into ~/.trellis/skills; --copy and project-local installation are not supported.";
|
|
366
|
+
if (json)
|
|
367
|
+
console.log(JSON.stringify({ action: "invalid-options", detail }, null, 2));
|
|
368
|
+
else
|
|
369
|
+
console.error(detail);
|
|
370
|
+
process.exitCode = 1;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (skill.error || branch.error || agents.error || positionals.length !== 1) {
|
|
374
|
+
const detail = skill.error ?? branch.error ?? agents.error ?? "Usage: trellis add <github-source> --skill <name>";
|
|
375
|
+
if (json)
|
|
376
|
+
console.log(JSON.stringify({ action: "invalid-options", detail }, null, 2));
|
|
377
|
+
else
|
|
378
|
+
console.error(detail);
|
|
379
|
+
process.exitCode = 1;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const source = positionals[0];
|
|
383
|
+
if (rest.includes("--list")) {
|
|
384
|
+
process.exitCode = (await runRemoteSkillList(source, { branch: branch.values[0], json })).exitCode;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (skill.values.length !== 1 || branch.values.length > 1) {
|
|
388
|
+
const detail = skill.values.length !== 1 ? "Use exactly one --skill value" : "Use at most one --branch value";
|
|
389
|
+
if (json)
|
|
390
|
+
console.log(JSON.stringify({ action: "invalid-options", detail }, null, 2));
|
|
391
|
+
else
|
|
392
|
+
console.error(detail);
|
|
393
|
+
process.exitCode = 1;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
process.exitCode = (await runRemoteSkillAdd(source, skill.values[0], {
|
|
397
|
+
branch: branch.values[0], agents: agents.values, dryRun: rest.includes("--dry-run"), json,
|
|
398
|
+
})).exitCode;
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (command === "update") {
|
|
402
|
+
const json = rest.includes("--json");
|
|
403
|
+
const names = rest.filter((arg) => !arg.startsWith("-"));
|
|
404
|
+
process.exitCode = (await runRemoteSkillUpdate(names, { dryRun: rest.includes("--dry-run"), json })).exitCode;
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
313
407
|
if (command === "mcp") {
|
|
314
408
|
const [subcommand, ...mcpRest] = rest;
|
|
315
409
|
const json = mcpRest.includes("--json");
|
|
@@ -325,12 +419,12 @@ async function main(argv) {
|
|
|
325
419
|
}
|
|
326
420
|
if (subcommand === "add") {
|
|
327
421
|
const [name] = mcpRest;
|
|
328
|
-
process.exitCode = runMcpAdd(name, parseMcpAddArgs(mcpRest), { json, dryRun }).exitCode;
|
|
422
|
+
process.exitCode = (await runMcpAdd(name, parseMcpAddArgs(mcpRest), { json, dryRun })).exitCode;
|
|
329
423
|
return;
|
|
330
424
|
}
|
|
331
425
|
if (subcommand === "import") {
|
|
332
426
|
const [file] = mcpRest;
|
|
333
|
-
process.exitCode = runMcpImport(file, { json, dryRun }).exitCode;
|
|
427
|
+
process.exitCode = (await runMcpImport(file, { json, dryRun })).exitCode;
|
|
334
428
|
return;
|
|
335
429
|
}
|
|
336
430
|
if (subcommand === "set") {
|
|
@@ -347,7 +441,7 @@ async function main(argv) {
|
|
|
347
441
|
process.exitCode = 1;
|
|
348
442
|
return;
|
|
349
443
|
}
|
|
350
|
-
process.exitCode = runMcpRemove(name, { json, dryRun }).exitCode;
|
|
444
|
+
process.exitCode = (await runMcpRemove(name, { json, dryRun })).exitCode;
|
|
351
445
|
return;
|
|
352
446
|
}
|
|
353
447
|
if (subcommand === "auth") {
|
|
@@ -382,7 +476,7 @@ async function main(argv) {
|
|
|
382
476
|
process.exitCode = 1;
|
|
383
477
|
return;
|
|
384
478
|
}
|
|
385
|
-
process.exitCode = runSkillAdd(name, from, { json, dryRun }).exitCode;
|
|
479
|
+
process.exitCode = (await runSkillAdd(name, from, { json, dryRun })).exitCode;
|
|
386
480
|
return;
|
|
387
481
|
}
|
|
388
482
|
if (subcommand === "remove") {
|
|
@@ -392,7 +486,7 @@ async function main(argv) {
|
|
|
392
486
|
process.exitCode = 1;
|
|
393
487
|
return;
|
|
394
488
|
}
|
|
395
|
-
process.exitCode = runSkillRemove(name, { json, dryRun }).exitCode;
|
|
489
|
+
process.exitCode = (await runSkillRemove(name, { json, dryRun })).exitCode;
|
|
396
490
|
return;
|
|
397
491
|
}
|
|
398
492
|
if (subcommand === "update-builtin") {
|
|
@@ -407,6 +501,21 @@ async function main(argv) {
|
|
|
407
501
|
const [subcommand, ...memoryRest] = rest;
|
|
408
502
|
const json = memoryRest.includes("--json");
|
|
409
503
|
const dryRun = memoryRest.includes("--dry-run");
|
|
504
|
+
if (subcommand === "list") {
|
|
505
|
+
const entries = collectMemoryList();
|
|
506
|
+
if (json) {
|
|
507
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
508
|
+
}
|
|
509
|
+
else if (entries.length === 0) {
|
|
510
|
+
console.log("No memories in canonical source yet.");
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
for (const { name, scope } of entries) {
|
|
514
|
+
console.log(`${name} — ${scope.length > 0 ? scope.join(", ") : "(no managed agent reaches it)"}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
410
519
|
if (subcommand === "sync") {
|
|
411
520
|
process.exitCode = runMemorySync({ json, dryRun }).exitCode;
|
|
412
521
|
return;
|
|
@@ -415,7 +524,7 @@ async function main(argv) {
|
|
|
415
524
|
process.exitCode = runMemoryExtraction({ json, dryRun }).exitCode;
|
|
416
525
|
return;
|
|
417
526
|
}
|
|
418
|
-
console.error(`Unknown memory subcommand: ${subcommand ?? "(none)"}\nUsage: trellis memory sync|extract\n`);
|
|
527
|
+
console.error(`Unknown memory subcommand: ${subcommand ?? "(none)"}\nUsage: trellis memory list|sync|extract\n`);
|
|
419
528
|
process.exitCode = 1;
|
|
420
529
|
return;
|
|
421
530
|
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -10,6 +10,7 @@ import * as codexProbe from "../probes/codex.js";
|
|
|
10
10
|
import * as kiroProbe from "../probes/kiro.js";
|
|
11
11
|
import * as piProbe from "../probes/pi.js";
|
|
12
12
|
import * as kimiCodeProbe from "../probes/kimi-code.js";
|
|
13
|
+
import * as zcodeProbe from "../probes/zcode.js";
|
|
13
14
|
import { loadCanonicalSource } from "../core/canonical.js";
|
|
14
15
|
import { GATEWAY_ENTRY_NAME, RUNTIME_ENTRY_NAME, resolveMcpPlan } from "../adapters/mcpPlan.js";
|
|
15
16
|
import { ALL_AGENTS } from "../core/types.js";
|
|
@@ -76,6 +77,7 @@ export async function collectDoctorReport(homeDir = homedir(), knownHostInjected
|
|
|
76
77
|
{ agent: "kiro", run: () => kiroProbe.probe(homeDir, { probeMcp }) },
|
|
77
78
|
{ agent: "pi", run: () => piProbe.probe(homeDir) },
|
|
78
79
|
{ agent: "kimi-code", run: () => kimiCodeProbe.probe(homeDir, { probeMcp }) },
|
|
80
|
+
{ agent: "zcode", run: () => zcodeProbe.probe(homeDir, { probeMcp }) },
|
|
79
81
|
];
|
|
80
82
|
// One agent erroring must not blank the other three's results.
|
|
81
83
|
const settled = await Promise.allSettled(probeList.map((p) => p.run()));
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface InitReport {
|
|
|
29
29
|
/** Exported so `migrate` can byte-compare canonical `agents.md` against
|
|
30
30
|
* this exact string (design.md D3, trellis-cli-migrate) rather than
|
|
31
31
|
* retyping an equivalent one that could silently drift from this. */
|
|
32
|
-
export declare const AGENTS_MD_TEMPLATE = "# Shared instructions\n\nWrite what every agent should know here \u2014 communication preferences,\nproject conventions, anything you'd otherwise repeat per agent.\n\nAlready using Claude Code, Codex, Kiro, pi,
|
|
32
|
+
export declare const AGENTS_MD_TEMPLATE = "# Shared instructions\n\nWrite what every agent should know here \u2014 communication preferences,\nproject conventions, anything you'd otherwise repeat per agent.\n\nAlready using Claude Code, Codex, Kiro, pi, Kimi Code, or ZCode? `trellis migrate --from\n<agent>` imports its real instructions and skills instead of starting\nfrom this placeholder.\n";
|
|
33
33
|
/**
|
|
34
34
|
* Trellis never spawns an installer itself (global package installs are
|
|
35
35
|
* exactly the kind of irreversible, system-wide action that needs the
|
package/dist/commands/init.js
CHANGED
|
@@ -16,6 +16,7 @@ import * as codexProbe from "../probes/codex.js";
|
|
|
16
16
|
import * as kiroProbe from "../probes/kiro.js";
|
|
17
17
|
import * as piProbe from "../probes/pi.js";
|
|
18
18
|
import * as kimiCodeProbe from "../probes/kimi-code.js";
|
|
19
|
+
import * as zcodeProbe from "../probes/zcode.js";
|
|
19
20
|
import { ALL_AGENTS } from "../core/types.js";
|
|
20
21
|
/** Exported so `migrate` can byte-compare canonical `agents.md` against
|
|
21
22
|
* this exact string (design.md D3, trellis-cli-migrate) rather than
|
|
@@ -25,7 +26,7 @@ export const AGENTS_MD_TEMPLATE = `# Shared instructions
|
|
|
25
26
|
Write what every agent should know here — communication preferences,
|
|
26
27
|
project conventions, anything you'd otherwise repeat per agent.
|
|
27
28
|
|
|
28
|
-
Already using Claude Code, Codex, Kiro, pi,
|
|
29
|
+
Already using Claude Code, Codex, Kiro, pi, Kimi Code, or ZCode? \`trellis migrate --from
|
|
29
30
|
<agent>\` imports its real instructions and skills instead of starting
|
|
30
31
|
from this placeholder.
|
|
31
32
|
`;
|
|
@@ -95,6 +96,7 @@ export const INSTALL_HINTS = {
|
|
|
95
96
|
kiro: "https://kiro.dev/downloads/",
|
|
96
97
|
pi: "npm install -g @earendil-works/pi-coding-agent",
|
|
97
98
|
"kimi-code": "curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash",
|
|
99
|
+
zcode: "https://zcode.z.ai/en/docs/install",
|
|
98
100
|
};
|
|
99
101
|
function ensureFile(path, template, write = true) {
|
|
100
102
|
if (existsSync(path)) {
|
|
@@ -132,6 +134,7 @@ export async function collectInitReport(homeDir = homedir(), options = {}) {
|
|
|
132
134
|
{ agent: "kiro", run: () => kiroProbe.probe(homeDir) },
|
|
133
135
|
{ agent: "pi", run: () => piProbe.probe(homeDir) },
|
|
134
136
|
{ agent: "kimi-code", run: () => kimiCodeProbe.probe(homeDir) },
|
|
137
|
+
{ agent: "zcode", run: () => zcodeProbe.probe(homeDir) },
|
|
135
138
|
];
|
|
136
139
|
const settled = await Promise.allSettled(probes.map((p) => p.run()));
|
|
137
140
|
const agents = settled.map((result, index) => {
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* gated — remove), and reports what happened. Kept as a separate
|
|
6
6
|
* command from bare `trellis sync` (skills/instructions) since the two
|
|
7
7
|
* have different write mechanisms entirely, not because MCP removal is
|
|
8
|
-
* unsafe (see `src/lib/mcpOwnership.ts`). `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* unsafe (see `src/lib/mcpOwnership.ts`). `add`/`remove` mutate the
|
|
9
|
+
* canonical source and then run the same sync `mcp sync` performs, so
|
|
10
|
+
* one command both edits the list and maps it onto the managed agents
|
|
11
|
+
* (removal propagates through the ownership ledger). `list` is
|
|
12
|
+
* read-only. All three are an alternative to hand-editing
|
|
13
|
+
* `~/.trellis/mcp/servers.yaml`.
|
|
12
14
|
*/
|
|
13
15
|
import { type ServersYamlWriteResult } from "../core/canonical.js";
|
|
14
16
|
import type { AdapterPlanItem } from "../core/adapter.js";
|
|
@@ -128,29 +130,69 @@ export declare function runMcpSetAuth(name: string | undefined, auth: string | u
|
|
|
128
130
|
}): {
|
|
129
131
|
exitCode: number;
|
|
130
132
|
};
|
|
131
|
-
export declare function applyMcpAddPlan(plan: McpAddPlan, homeDir?: string): ServersYamlWriteResult;
|
|
133
|
+
export declare function applyMcpAddPlan(plan: McpAddPlan, homeDir?: string, backup?: BackupSession): ServersYamlWriteResult;
|
|
134
|
+
export interface McpAddOutcome {
|
|
135
|
+
plan: McpAddPlan;
|
|
136
|
+
writeError?: string;
|
|
137
|
+
sync?: McpSyncReport;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The apply-and-cascade-sync orchestration `runMcpAdd` used to do inline
|
|
141
|
+
* and only expose via a console.log'd payload — split out so a
|
|
142
|
+
* non-CLI caller (trellis-gui's sidecar) can get the same structured
|
|
143
|
+
* result a JSON-mode CLI run prints, without scraping stdout or
|
|
144
|
+
* re-deriving the same sequencing itself (trellis-gui design.md
|
|
145
|
+
* Decision 1). Behavior-preserving: `runMcpAdd` below is now a thin
|
|
146
|
+
* wrapper over this plus its own printing, same as every other command
|
|
147
|
+
* in this file already splits compute from CLI I/O.
|
|
148
|
+
*
|
|
149
|
+
* Opens its own backup session around the direct `servers.yaml` write
|
|
150
|
+
* when the caller doesn't already have one open — this canonical write
|
|
151
|
+
* used to be the one path in this file that bypassed backup/rollback
|
|
152
|
+
* entirely (trellis-gui tasks.md 3.5 found this empirically: a real `mcp
|
|
153
|
+
* add` left zero trace in `trellis rollback --list`). Mirrors the
|
|
154
|
+
* `ownSession` pattern `collectMcpSyncReport` below already uses for
|
|
155
|
+
* exactly the same reason.
|
|
156
|
+
*/
|
|
157
|
+
export declare function applyMcpAddWithSync(plan: McpAddPlan, opts?: {
|
|
158
|
+
homeDir?: string;
|
|
159
|
+
dryRun?: boolean;
|
|
160
|
+
backupSession?: BackupSession;
|
|
161
|
+
}): Promise<McpAddOutcome>;
|
|
132
162
|
export declare function runMcpAdd(name: string | undefined, raw: McpAddRawArgs, opts?: {
|
|
133
163
|
homeDir?: string;
|
|
134
164
|
json?: boolean;
|
|
135
165
|
dryRun?: boolean;
|
|
136
|
-
}): {
|
|
166
|
+
}): Promise<{
|
|
137
167
|
exitCode: number;
|
|
138
|
-
}
|
|
168
|
+
}>;
|
|
139
169
|
export type McpRemoveAction = "removed" | "not-found";
|
|
140
170
|
export interface McpRemovePlan {
|
|
141
171
|
name: string;
|
|
142
172
|
action: McpRemoveAction;
|
|
143
173
|
}
|
|
144
|
-
/** Canonical
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
174
|
+
/** Canonical plan only — `runMcpRemove` follows a real removal with the
|
|
175
|
+
* same sync `mcp sync` performs, which propagates the deletion to every
|
|
176
|
+
* agent whose entry the ownership ledger proves Trellis wrote
|
|
177
|
+
* (trellis-mcp-lifecycle-parity, roadmap P14). */
|
|
148
178
|
export declare function collectMcpRemovePlan(name: string, homeDir?: string): McpRemovePlan;
|
|
149
|
-
export declare function applyMcpRemovePlan(plan: McpRemovePlan, homeDir?: string): ServersYamlWriteResult;
|
|
179
|
+
export declare function applyMcpRemovePlan(plan: McpRemovePlan, homeDir?: string, backup?: BackupSession): ServersYamlWriteResult;
|
|
180
|
+
export interface McpRemoveOutcome {
|
|
181
|
+
plan: McpRemovePlan;
|
|
182
|
+
writeError?: string;
|
|
183
|
+
sync?: McpSyncReport;
|
|
184
|
+
}
|
|
185
|
+
/** See `applyMcpAddWithSync`'s doc comment — same split, same reason, same
|
|
186
|
+
* own-backup-session fix. */
|
|
187
|
+
export declare function applyMcpRemoveWithSync(plan: McpRemovePlan, opts?: {
|
|
188
|
+
homeDir?: string;
|
|
189
|
+
dryRun?: boolean;
|
|
190
|
+
backupSession?: BackupSession;
|
|
191
|
+
}): Promise<McpRemoveOutcome>;
|
|
150
192
|
export declare function runMcpRemove(name: string, opts?: {
|
|
151
193
|
homeDir?: string;
|
|
152
194
|
json?: boolean;
|
|
153
195
|
dryRun?: boolean;
|
|
154
|
-
}): {
|
|
196
|
+
}): Promise<{
|
|
155
197
|
exitCode: number;
|
|
156
|
-
}
|
|
198
|
+
}>;
|
package/dist/commands/mcp.js
CHANGED
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* gated — remove), and reports what happened. Kept as a separate
|
|
6
6
|
* command from bare `trellis sync` (skills/instructions) since the two
|
|
7
7
|
* have different write mechanisms entirely, not because MCP removal is
|
|
8
|
-
* unsafe (see `src/lib/mcpOwnership.ts`). `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* unsafe (see `src/lib/mcpOwnership.ts`). `add`/`remove` mutate the
|
|
9
|
+
* canonical source and then run the same sync `mcp sync` performs, so
|
|
10
|
+
* one command both edits the list and maps it onto the managed agents
|
|
11
|
+
* (removal propagates through the ownership ledger). `list` is
|
|
12
|
+
* read-only. All three are an alternative to hand-editing
|
|
13
|
+
* `~/.trellis/mcp/servers.yaml`.
|
|
12
14
|
*/
|
|
13
15
|
import { homedir } from "node:os";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
14
17
|
import { join } from "node:path";
|
|
15
18
|
import { loadCanonicalSource, upsertServerYaml, removeServerYaml } from "../core/canonical.js";
|
|
16
19
|
import { ALL_AGENTS, resolveScope } from "../core/types.js";
|
|
@@ -19,6 +22,7 @@ import { CodexAdapter } from "../adapters/codex.js";
|
|
|
19
22
|
import { KiroAdapter } from "../adapters/kiro.js";
|
|
20
23
|
import { PiAdapter } from "../adapters/pi.js";
|
|
21
24
|
import { KimiCodeAdapter } from "../adapters/kimi-code.js";
|
|
25
|
+
import { ZcodeAdapter } from "../adapters/zcode.js";
|
|
22
26
|
import { openBackupSession } from "../lib/backup.js";
|
|
23
27
|
const ADAPTER_FACTORY = {
|
|
24
28
|
"claude-code": (homeDir) => new ClaudeCodeAdapter(homeDir),
|
|
@@ -26,6 +30,7 @@ const ADAPTER_FACTORY = {
|
|
|
26
30
|
kiro: (homeDir) => new KiroAdapter(homeDir),
|
|
27
31
|
pi: (homeDir) => new PiAdapter(homeDir),
|
|
28
32
|
"kimi-code": (homeDir) => new KimiCodeAdapter(homeDir),
|
|
33
|
+
zcode: (homeDir) => new ZcodeAdapter(homeDir),
|
|
29
34
|
};
|
|
30
35
|
/** Only agents in `canonical.managedAgents` — see src/commands/sync.ts's
|
|
31
36
|
* own copy of this same restriction (trellis-managed-agents). */
|
|
@@ -297,12 +302,48 @@ export function runMcpSetAuth(name, auth, opts = {}) {
|
|
|
297
302
|
}
|
|
298
303
|
return { exitCode: ["invalid-input", "not-found"].includes(plan.action) || Boolean(writeError) ? 1 : 0 };
|
|
299
304
|
}
|
|
300
|
-
export function applyMcpAddPlan(plan, homeDir = homedir()) {
|
|
305
|
+
export function applyMcpAddPlan(plan, homeDir = homedir(), backup) {
|
|
301
306
|
if (plan.action !== "create" || !plan.def)
|
|
302
307
|
return { ok: true };
|
|
303
|
-
return upsertServerYaml(serversYamlPath(homeDir), plan.name, plan.def);
|
|
308
|
+
return upsertServerYaml(serversYamlPath(homeDir), plan.name, plan.def, backup);
|
|
304
309
|
}
|
|
305
|
-
|
|
310
|
+
/**
|
|
311
|
+
* The apply-and-cascade-sync orchestration `runMcpAdd` used to do inline
|
|
312
|
+
* and only expose via a console.log'd payload — split out so a
|
|
313
|
+
* non-CLI caller (trellis-gui's sidecar) can get the same structured
|
|
314
|
+
* result a JSON-mode CLI run prints, without scraping stdout or
|
|
315
|
+
* re-deriving the same sequencing itself (trellis-gui design.md
|
|
316
|
+
* Decision 1). Behavior-preserving: `runMcpAdd` below is now a thin
|
|
317
|
+
* wrapper over this plus its own printing, same as every other command
|
|
318
|
+
* in this file already splits compute from CLI I/O.
|
|
319
|
+
*
|
|
320
|
+
* Opens its own backup session around the direct `servers.yaml` write
|
|
321
|
+
* when the caller doesn't already have one open — this canonical write
|
|
322
|
+
* used to be the one path in this file that bypassed backup/rollback
|
|
323
|
+
* entirely (trellis-gui tasks.md 3.5 found this empirically: a real `mcp
|
|
324
|
+
* add` left zero trace in `trellis rollback --list`). Mirrors the
|
|
325
|
+
* `ownSession` pattern `collectMcpSyncReport` below already uses for
|
|
326
|
+
* exactly the same reason.
|
|
327
|
+
*/
|
|
328
|
+
export async function applyMcpAddWithSync(plan, opts = {}) {
|
|
329
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
330
|
+
const ownSession = !opts.dryRun && !opts.backupSession && plan.action === "create" ? openBackupSession(homeDir, "mcp-add") : undefined;
|
|
331
|
+
const session = opts.backupSession ?? ownSession;
|
|
332
|
+
let writeError;
|
|
333
|
+
if (!opts.dryRun && plan.action === "create") {
|
|
334
|
+
const result = applyMcpAddPlan(plan, homeDir, session);
|
|
335
|
+
if (!result.ok)
|
|
336
|
+
writeError = result.error;
|
|
337
|
+
}
|
|
338
|
+
ownSession?.finalize();
|
|
339
|
+
const planFailed = plan.action === "conflict" || plan.action === "invalid-input" || Boolean(writeError);
|
|
340
|
+
let syncReport;
|
|
341
|
+
if (!planFailed && plan.action === "create" && existsSync(join(homeDir, ".trellis"))) {
|
|
342
|
+
syncReport = await collectMcpSyncReport({ homeDir, dryRun: opts.dryRun });
|
|
343
|
+
}
|
|
344
|
+
return { plan, ...(writeError ? { writeError } : {}), ...(syncReport ? { sync: syncReport } : {}) };
|
|
345
|
+
}
|
|
346
|
+
export async function runMcpAdd(name, raw, opts = {}) {
|
|
306
347
|
const homeDir = opts.homeDir ?? homedir();
|
|
307
348
|
let plan;
|
|
308
349
|
try {
|
|
@@ -312,37 +353,65 @@ export function runMcpAdd(name, raw, opts = {}) {
|
|
|
312
353
|
console.error(err instanceof Error ? err.message : String(err));
|
|
313
354
|
return { exitCode: 1 };
|
|
314
355
|
}
|
|
315
|
-
let
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
356
|
+
let outcome;
|
|
357
|
+
try {
|
|
358
|
+
outcome = await applyMcpAddWithSync(plan, { homeDir, dryRun: opts.dryRun });
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
362
|
+
return { exitCode: 1 };
|
|
320
363
|
}
|
|
364
|
+
const { writeError, sync: syncReport } = outcome;
|
|
321
365
|
if (opts.json) {
|
|
322
|
-
|
|
366
|
+
const payload = writeError ? { ...plan, writeError } : plan;
|
|
367
|
+
console.log(JSON.stringify(syncReport ? { ...payload, sync: syncReport } : payload, null, 2));
|
|
323
368
|
}
|
|
324
369
|
else {
|
|
325
370
|
console.log(`${opts.dryRun ? "[dry run] " : ""}mcp add ${plan.name}`);
|
|
326
371
|
console.log(` [${plan.action}] ${plan.detail}`);
|
|
327
372
|
if (writeError)
|
|
328
373
|
console.error(` write failed: ${writeError}`);
|
|
374
|
+
if (syncReport)
|
|
375
|
+
printReport(syncReport, opts.dryRun ?? false);
|
|
329
376
|
}
|
|
330
|
-
|
|
377
|
+
const planFailed = plan.action === "conflict" || plan.action === "invalid-input" || Boolean(writeError);
|
|
378
|
+
const syncConflict = syncReport?.reports.some((r) => r.items.some((i) => i.action === "conflict")) ?? false;
|
|
379
|
+
return { exitCode: planFailed || syncConflict ? 1 : 0 };
|
|
331
380
|
}
|
|
332
|
-
/** Canonical
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
381
|
+
/** Canonical plan only — `runMcpRemove` follows a real removal with the
|
|
382
|
+
* same sync `mcp sync` performs, which propagates the deletion to every
|
|
383
|
+
* agent whose entry the ownership ledger proves Trellis wrote
|
|
384
|
+
* (trellis-mcp-lifecycle-parity, roadmap P14). */
|
|
336
385
|
export function collectMcpRemovePlan(name, homeDir = homedir()) {
|
|
337
386
|
const canonical = loadCanonicalSource(homeDir);
|
|
338
387
|
return { name, action: canonical.mcp.servers[name] ? "removed" : "not-found" };
|
|
339
388
|
}
|
|
340
|
-
export function applyMcpRemovePlan(plan, homeDir = homedir()) {
|
|
389
|
+
export function applyMcpRemovePlan(plan, homeDir = homedir(), backup) {
|
|
341
390
|
if (plan.action !== "removed")
|
|
342
391
|
return { ok: true };
|
|
343
|
-
return removeServerYaml(serversYamlPath(homeDir), plan.name);
|
|
392
|
+
return removeServerYaml(serversYamlPath(homeDir), plan.name, backup);
|
|
393
|
+
}
|
|
394
|
+
/** See `applyMcpAddWithSync`'s doc comment — same split, same reason, same
|
|
395
|
+
* own-backup-session fix. */
|
|
396
|
+
export async function applyMcpRemoveWithSync(plan, opts = {}) {
|
|
397
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
398
|
+
const ownSession = !opts.dryRun && !opts.backupSession && plan.action === "removed" ? openBackupSession(homeDir, "mcp-remove") : undefined;
|
|
399
|
+
const session = opts.backupSession ?? ownSession;
|
|
400
|
+
let writeError;
|
|
401
|
+
if (!opts.dryRun && plan.action === "removed") {
|
|
402
|
+
const result = applyMcpRemovePlan(plan, homeDir, session);
|
|
403
|
+
if (!result.ok)
|
|
404
|
+
writeError = result.error;
|
|
405
|
+
}
|
|
406
|
+
ownSession?.finalize();
|
|
407
|
+
const planFailed = plan.action === "not-found" || Boolean(writeError);
|
|
408
|
+
let syncReport;
|
|
409
|
+
if (!planFailed && existsSync(join(homeDir, ".trellis"))) {
|
|
410
|
+
syncReport = await collectMcpSyncReport({ homeDir, dryRun: opts.dryRun });
|
|
411
|
+
}
|
|
412
|
+
return { plan, ...(writeError ? { writeError } : {}), ...(syncReport ? { sync: syncReport } : {}) };
|
|
344
413
|
}
|
|
345
|
-
export function runMcpRemove(name, opts = {}) {
|
|
414
|
+
export async function runMcpRemove(name, opts = {}) {
|
|
346
415
|
const homeDir = opts.homeDir ?? homedir();
|
|
347
416
|
let plan;
|
|
348
417
|
try {
|
|
@@ -352,14 +421,19 @@ export function runMcpRemove(name, opts = {}) {
|
|
|
352
421
|
console.error(err instanceof Error ? err.message : String(err));
|
|
353
422
|
return { exitCode: 1 };
|
|
354
423
|
}
|
|
355
|
-
let
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (!result.ok)
|
|
359
|
-
writeError = result.error;
|
|
424
|
+
let outcome;
|
|
425
|
+
try {
|
|
426
|
+
outcome = await applyMcpRemoveWithSync(plan, { homeDir, dryRun: opts.dryRun });
|
|
360
427
|
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
430
|
+
return { exitCode: 1 };
|
|
431
|
+
}
|
|
432
|
+
const { writeError, sync: syncReport } = outcome;
|
|
433
|
+
const planFailed = plan.action === "not-found" || Boolean(writeError);
|
|
361
434
|
if (opts.json) {
|
|
362
|
-
|
|
435
|
+
const payload = writeError ? { ...plan, writeError } : plan;
|
|
436
|
+
console.log(JSON.stringify(syncReport ? { ...payload, sync: syncReport } : payload, null, 2));
|
|
363
437
|
}
|
|
364
438
|
else if (plan.action === "not-found") {
|
|
365
439
|
console.error(`"${name}" is not a canonical MCP server — nothing to remove.`);
|
|
@@ -369,6 +443,9 @@ export function runMcpRemove(name, opts = {}) {
|
|
|
369
443
|
}
|
|
370
444
|
else {
|
|
371
445
|
console.log(`${opts.dryRun ? "[dry run] " : ""}removed MCP server "${name}" from canonical source.`);
|
|
446
|
+
if (syncReport)
|
|
447
|
+
printReport(syncReport, opts.dryRun ?? false);
|
|
372
448
|
}
|
|
373
|
-
|
|
449
|
+
const syncConflict = syncReport?.reports.some((r) => r.items.some((i) => i.action === "conflict")) ?? false;
|
|
450
|
+
return { exitCode: planFailed || syncConflict ? 1 : 0 };
|
|
374
451
|
}
|
|
@@ -24,6 +24,7 @@ import { BuiltinRegistry, createRuntimeServer, UpstreamProvider } from "../lib/m
|
|
|
24
24
|
import { McpStatusProvider } from "../lib/mcpStatusProvider.js";
|
|
25
25
|
import { RuntimeControlProvider } from "../lib/runtimeControlProvider.js";
|
|
26
26
|
import { TaskProvider } from "../lib/taskProvider.js";
|
|
27
|
+
import { AgentBridgeProvider } from "../lib/agentBridgeProvider.js";
|
|
27
28
|
import { RuntimeMemoryProvider } from "../lib/memoryProvider.js";
|
|
28
29
|
import { SkillProvider } from "../lib/skillProvider.js";
|
|
29
30
|
import { ALL_AGENTS } from "../core/types.js";
|
|
@@ -121,7 +122,7 @@ async function runMcpEdge(opts, clientInfo, serverInfo) {
|
|
|
121
122
|
// process has no terminal to run one in (design.md D9).
|
|
122
123
|
homeDir,
|
|
123
124
|
});
|
|
124
|
-
const registry = new BuiltinRegistry([new SkillProvider(), new RuntimeMemoryProvider(), new RuntimeControlProvider(backend), new McpStatusProvider(backend), new TaskProvider(), new UpstreamProvider(backend)]);
|
|
125
|
+
const registry = new BuiltinRegistry([new SkillProvider(), new RuntimeMemoryProvider(), new RuntimeControlProvider(backend), new McpStatusProvider(backend), new TaskProvider(), new AgentBridgeProvider(), new UpstreamProvider(backend)]);
|
|
125
126
|
const server = createRuntimeServer(serverInfo, { agentId: opts.agentId, homeDir }, registry);
|
|
126
127
|
const transport = new StdioServerTransport();
|
|
127
128
|
await server.connect(transport);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* read-only; plans never carry secret values.
|
|
7
7
|
*/
|
|
8
8
|
import { type BackupSession } from "../lib/backup.js";
|
|
9
|
+
import type { McpSyncReport } from "./mcp.js";
|
|
9
10
|
export type McpImportAction = "create" | "already-present" | "duplicate" | "skipped" | "conflict" | "invalid-input";
|
|
10
11
|
export interface McpImportItem {
|
|
11
12
|
name: string;
|
|
@@ -18,10 +19,28 @@ export interface McpImportPlan {
|
|
|
18
19
|
}
|
|
19
20
|
export declare function collectMcpImportPlan(path: string, homeDir?: string): McpImportPlan;
|
|
20
21
|
export declare function applyMcpImportPlan(path: string, homeDir?: string, backup?: BackupSession): McpImportPlan;
|
|
22
|
+
export interface McpImportOutcome {
|
|
23
|
+
plan: McpImportPlan;
|
|
24
|
+
sync?: McpSyncReport;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* See `applyMcpAddWithSync`'s doc comment (mcp.ts) — same split, same
|
|
28
|
+
* reason: a non-CLI caller (trellis-gui's sidecar) gets the same
|
|
29
|
+
* structured result a JSON-mode CLI run prints, without scraping
|
|
30
|
+
* stdout or re-deriving the plan-then-apply-then-cascade-sync
|
|
31
|
+
* sequencing itself. Unlike `mcp add`/`mcp remove`, `applyMcpImportPlan`
|
|
32
|
+
* already opens its own backup session unconditionally (it always has,
|
|
33
|
+
* per design.md's original survey of `openBackupSession` call sites) —
|
|
34
|
+
* no tasks.md 3.5-style fix needed here.
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyMcpImportWithSync(path: string, opts?: {
|
|
37
|
+
homeDir?: string;
|
|
38
|
+
dryRun?: boolean;
|
|
39
|
+
}): Promise<McpImportOutcome>;
|
|
21
40
|
export declare function runMcpImport(path: string | undefined, opts?: {
|
|
22
41
|
homeDir?: string;
|
|
23
42
|
json?: boolean;
|
|
24
43
|
dryRun?: boolean;
|
|
25
|
-
}): {
|
|
44
|
+
}): Promise<{
|
|
26
45
|
exitCode: number;
|
|
27
|
-
}
|
|
46
|
+
}>;
|