agent-trellis 0.8.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 +43 -207
- package/dist/adapters/zcode.d.ts +21 -0
- package/dist/adapters/zcode.js +234 -0
- package/dist/cli.js +25 -9
- 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/rollback.js +24 -1
- package/dist/commands/secretsAudit.js +4 -1
- package/dist/commands/skill.d.ts +35 -6
- package/dist/commands/skill.js +67 -13
- package/dist/commands/sync.js +3 -1
- package/dist/core/adapter.d.ts +11 -1
- 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 +30 -0
- package/dist/lib/backup.js +16 -1
- package/dist/lib/chatAgents.d.ts +42 -0
- package/dist/lib/chatAgents.js +59 -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/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 +173 -12
- 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/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
|
+
}>;
|
|
@@ -11,6 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
import { loadCanonicalSource, ensureGitignoreEntry, ensureShellEnvSource, upsertServerYaml, writeSecretsPolicyExtraction } from "../core/canonical.js";
|
|
12
12
|
import { openBackupSession } from "../lib/backup.js";
|
|
13
13
|
import { parseDotenv, writeLocalSecretValue } from "../lib/secretEnv.js";
|
|
14
|
+
import { collectMcpSyncReport, printReport as printMcpSyncReport } from "./mcp.js";
|
|
14
15
|
const DEFAULT_ENV_FILE_TILDE = "~/.trellis/mcp/servers.local.env";
|
|
15
16
|
class SkipImport extends Error {
|
|
16
17
|
}
|
|
@@ -309,25 +310,47 @@ export function applyMcpImportPlan(path, homeDir = homedir(), backup) {
|
|
|
309
310
|
ownSession.finalize();
|
|
310
311
|
return publicPlan(plan);
|
|
311
312
|
}
|
|
312
|
-
|
|
313
|
+
/**
|
|
314
|
+
* See `applyMcpAddWithSync`'s doc comment (mcp.ts) — same split, same
|
|
315
|
+
* reason: a non-CLI caller (trellis-gui's sidecar) gets the same
|
|
316
|
+
* structured result a JSON-mode CLI run prints, without scraping
|
|
317
|
+
* stdout or re-deriving the plan-then-apply-then-cascade-sync
|
|
318
|
+
* sequencing itself. Unlike `mcp add`/`mcp remove`, `applyMcpImportPlan`
|
|
319
|
+
* already opens its own backup session unconditionally (it always has,
|
|
320
|
+
* per design.md's original survey of `openBackupSession` call sites) —
|
|
321
|
+
* no tasks.md 3.5-style fix needed here.
|
|
322
|
+
*/
|
|
323
|
+
export async function applyMcpImportWithSync(path, opts = {}) {
|
|
324
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
325
|
+
const plan = opts.dryRun ? collectMcpImportPlan(path, homeDir) : applyMcpImportPlan(path, homeDir);
|
|
326
|
+
const failed = plan.items.some((item) => item.action === "conflict" || item.action === "invalid-input");
|
|
327
|
+
const changed = plan.items.some((item) => item.action === "create");
|
|
328
|
+
let syncReport;
|
|
329
|
+
if (!failed && changed && existsSync(join(homeDir, ".trellis"))) {
|
|
330
|
+
syncReport = await collectMcpSyncReport({ homeDir, dryRun: opts.dryRun });
|
|
331
|
+
}
|
|
332
|
+
return { plan, ...(syncReport ? { sync: syncReport } : {}) };
|
|
333
|
+
}
|
|
334
|
+
export async function runMcpImport(path, opts = {}) {
|
|
313
335
|
const homeDir = opts.homeDir ?? homedir();
|
|
314
336
|
if (!path) {
|
|
315
337
|
console.error("Usage: trellis mcp import <json-file> [--dry-run]");
|
|
316
338
|
return { exitCode: 1 };
|
|
317
339
|
}
|
|
318
340
|
try {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
applyMcpImportPlan(path, homeDir);
|
|
322
|
-
const result = publicPlan(internal);
|
|
341
|
+
const { plan: result, sync: syncReport } = await applyMcpImportWithSync(path, { homeDir, dryRun: opts.dryRun });
|
|
342
|
+
const failed = result.items.some((item) => item.action === "conflict" || item.action === "invalid-input");
|
|
323
343
|
if (opts.json)
|
|
324
|
-
console.log(JSON.stringify(result, null, 2));
|
|
344
|
+
console.log(JSON.stringify(syncReport ? { ...result, sync: syncReport } : result, null, 2));
|
|
325
345
|
else {
|
|
326
346
|
console.log(`${opts.dryRun ? "[dry run] " : ""}mcp import ${path}`);
|
|
327
347
|
for (const item of result.items)
|
|
328
348
|
console.log(` [${item.action}] ${item.name} — ${item.detail}`);
|
|
349
|
+
if (syncReport)
|
|
350
|
+
printMcpSyncReport(syncReport, opts.dryRun ?? false);
|
|
329
351
|
}
|
|
330
|
-
|
|
352
|
+
const syncConflict = syncReport?.reports.some((r) => r.items.some((i) => i.action === "conflict")) ?? false;
|
|
353
|
+
return { exitCode: failed || syncConflict ? 1 : 0 };
|
|
331
354
|
}
|
|
332
355
|
catch (err) {
|
|
333
356
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -12,9 +12,21 @@
|
|
|
12
12
|
* server process — both are plain file operations, same posture as
|
|
13
13
|
* every other Trellis write.
|
|
14
14
|
*/
|
|
15
|
-
import type { McpConfig, McpServerDef } from "../core/types.js";
|
|
15
|
+
import type { AgentId, McpConfig, McpServerDef } from "../core/types.js";
|
|
16
16
|
import type { BackupSession } from "../lib/backup.js";
|
|
17
17
|
import { type MemoryExtractionPlan, type MemorySyncPlan } from "../lib/memoryGraph.js";
|
|
18
|
+
export interface MemoryListEntry {
|
|
19
|
+
name: string;
|
|
20
|
+
scope: readonly AgentId[];
|
|
21
|
+
}
|
|
22
|
+
/** Canonical `memories/*.md` profile files — the list `trellis-gui`'s
|
|
23
|
+
* Memory view reads (tasks.md 6.2), mirroring `collectSkillList`'s
|
|
24
|
+
* shape exactly (skill.ts) since both are "canonical files with a
|
|
25
|
+
* resolved managed-agent scope" lists. Distinct from this file's own
|
|
26
|
+
* `MEMORY_SERVER_NAME` sync/extraction pipeline below, which is about
|
|
27
|
+
* the separate shared-Memory-MCP-server knowledge graph, not these
|
|
28
|
+
* per-topic markdown files. */
|
|
29
|
+
export declare function collectMemoryList(homeDir?: string): MemoryListEntry[];
|
|
18
30
|
/** Exported so `onboard` (`trellis onboard --memory on|off`,
|
|
19
31
|
* trellis-onboard-mcp-mode) can look up and write this same entry
|
|
20
32
|
* without hardcoding the name a second time. */
|
package/dist/commands/memory.js
CHANGED
|
@@ -16,7 +16,22 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
16
16
|
import { homedir } from "node:os";
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
18
|
import { loadCanonicalSource } from "../core/canonical.js";
|
|
19
|
+
import { resolveScope } from "../core/types.js";
|
|
19
20
|
import { parseMemoryGraph, planMemoryExtraction, planMemorySync, renderMemoryGraph } from "../lib/memoryGraph.js";
|
|
21
|
+
/** Canonical `memories/*.md` profile files — the list `trellis-gui`'s
|
|
22
|
+
* Memory view reads (tasks.md 6.2), mirroring `collectSkillList`'s
|
|
23
|
+
* shape exactly (skill.ts) since both are "canonical files with a
|
|
24
|
+
* resolved managed-agent scope" lists. Distinct from this file's own
|
|
25
|
+
* `MEMORY_SERVER_NAME` sync/extraction pipeline below, which is about
|
|
26
|
+
* the separate shared-Memory-MCP-server knowledge graph, not these
|
|
27
|
+
* per-topic markdown files. */
|
|
28
|
+
export function collectMemoryList(homeDir = homedir()) {
|
|
29
|
+
const canonical = loadCanonicalSource(homeDir);
|
|
30
|
+
return canonical.memories.map((memory) => ({
|
|
31
|
+
name: memory.name,
|
|
32
|
+
scope: resolveScope(memory.scope, canonical.managedAgents),
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
20
35
|
/** Exported so `onboard` (`trellis onboard --memory on|off`,
|
|
21
36
|
* trellis-onboard-mcp-mode) can look up and write this same entry
|
|
22
37
|
* without hardcoding the name a second time. */
|
package/dist/commands/migrate.js
CHANGED
|
@@ -12,10 +12,11 @@ import * as codexProbe from "../probes/codex.js";
|
|
|
12
12
|
import * as kiroProbe from "../probes/kiro.js";
|
|
13
13
|
import * as piProbe from "../probes/pi.js";
|
|
14
14
|
import * as kimiCodeProbe from "../probes/kimi-code.js";
|
|
15
|
+
import * as zcodeProbe from "../probes/zcode.js";
|
|
15
16
|
import { AGENTS_MD_TEMPLATE } from "./init.js";
|
|
16
17
|
import { decideDirImport } from "../lib/dirEquals.js";
|
|
17
18
|
import { deepEqual } from "../lib/deepEqual.js";
|
|
18
|
-
import { readClaudeCodeMcpDefs, readCodexMcpDefs, readKiroMcpDefs, readKimiCodeMcpDefs, resolvedEnvTextMap } from "../lib/mcpMigrateRead.js";
|
|
19
|
+
import { readClaudeCodeMcpDefs, readCodexMcpDefs, readKiroMcpDefs, readKimiCodeMcpDefs, readZcodeMcpDefs, resolvedEnvTextMap } from "../lib/mcpMigrateRead.js";
|
|
19
20
|
import { parseDotenv, writeLocalSecretValue } from "../lib/secretEnv.js";
|
|
20
21
|
import { findLiteralSecret } from "../adapters/mcpPlan.js";
|
|
21
22
|
import { ensureGitignoreEntry, ensureShellEnvSource, loadCanonicalSource, upsertServerYaml, writeSecretsPolicyExtraction } from "../core/canonical.js";
|
|
@@ -51,6 +52,7 @@ const PROBES = {
|
|
|
51
52
|
kiro: (homeDir) => kiroProbe.probe(homeDir),
|
|
52
53
|
pi: (homeDir) => piProbe.probe(homeDir),
|
|
53
54
|
"kimi-code": (homeDir) => kimiCodeProbe.probe(homeDir),
|
|
55
|
+
zcode: (homeDir) => zcodeProbe.probe(homeDir),
|
|
54
56
|
};
|
|
55
57
|
/** pi has no static MCP config to read at all (roadmap.md P14/
|
|
56
58
|
* trellis-migrate-mcp-servers) — deliberately absent, not an oversight;
|
|
@@ -60,6 +62,7 @@ const MCP_READERS = {
|
|
|
60
62
|
kiro: readKiroMcpDefs,
|
|
61
63
|
codex: readCodexMcpDefs,
|
|
62
64
|
"kimi-code": readKimiCodeMcpDefs,
|
|
65
|
+
zcode: readZcodeMcpDefs,
|
|
63
66
|
};
|
|
64
67
|
function planSkill(name, sourceDir, isSymlink, caseCorrect, canonicalDir) {
|
|
65
68
|
if (isSymlink) {
|
package/dist/commands/onboard.js
CHANGED
|
@@ -23,6 +23,7 @@ import * as codexProbe from "../probes/codex.js";
|
|
|
23
23
|
import * as kiroProbe from "../probes/kiro.js";
|
|
24
24
|
import * as piProbe from "../probes/pi.js";
|
|
25
25
|
import * as kimiCodeProbe from "../probes/kimi-code.js";
|
|
26
|
+
import * as zcodeProbe from "../probes/zcode.js";
|
|
26
27
|
import { ALL_AGENTS, capabilityDeliveryForAgent } from "../core/types.js";
|
|
27
28
|
import { loadCanonicalSource, removeServerYaml, upsertServerYaml, writeMcpModeYaml, writeMcpRoutesYaml, writeMcpRuntimeDeliveryYaml } from "../core/canonical.js";
|
|
28
29
|
import { buildCapabilityInventory, parseCapabilitySelectionFile, resolveSelectedNames, selectionContains } from "../core/capabilitySelection.js";
|
|
@@ -47,6 +48,7 @@ const PROBES = {
|
|
|
47
48
|
kiro: (homeDir) => kiroProbe.probe(homeDir),
|
|
48
49
|
pi: (homeDir) => piProbe.probe(homeDir),
|
|
49
50
|
"kimi-code": (homeDir) => kimiCodeProbe.probe(homeDir),
|
|
51
|
+
zcode: (homeDir) => zcodeProbe.probe(homeDir),
|
|
50
52
|
};
|
|
51
53
|
/**
|
|
52
54
|
* Scores only real source-side content. The recommendation is a hint for a
|
|
@@ -824,14 +826,23 @@ export async function collectOnboardPlan(opts = {}) {
|
|
|
824
826
|
if (!requestedRoutes && shouldPromptForRoutes) {
|
|
825
827
|
requestedRoutes = await resolveInteractiveMcpRoutes(managedAgents, availableMcpNames, currentMcpAfterMigration.hub !== undefined);
|
|
826
828
|
}
|
|
827
|
-
|
|
829
|
+
// ZCode's generic ~/.agents/skills discovery bypasses Trellis scope. Its
|
|
830
|
+
// default is therefore Runtime-first unless a capability-selection file
|
|
831
|
+
// explicitly chooses native/both, or canonical already has a choice.
|
|
832
|
+
const zcodeRuntimeRecommendation = managedAgents.includes("zcode") && currentMcpAfterMigration.runtime?.delivery?.zcode === undefined
|
|
833
|
+
? { zcode: "mcp" }
|
|
834
|
+
: {};
|
|
835
|
+
const requestedRuntimeDelivery = {
|
|
836
|
+
...zcodeRuntimeRecommendation,
|
|
837
|
+
...(capabilitySelection?.runtimeDelivery ?? {}),
|
|
838
|
+
};
|
|
828
839
|
let mcpSyncOverride;
|
|
829
|
-
if ((requestedRoutes && Object.keys(requestedRoutes).length > 0) ||
|
|
840
|
+
if ((requestedRoutes && Object.keys(requestedRoutes).length > 0) || Object.keys(requestedRuntimeDelivery).length > 0) {
|
|
830
841
|
const currentMcp = currentMcpAfterMigration;
|
|
831
842
|
const routes = requestedRoutes && Object.keys(requestedRoutes).length > 0
|
|
832
843
|
? { ...(currentMcp.routes ?? {}), ...requestedRoutes }
|
|
833
844
|
: currentMcp.routes;
|
|
834
|
-
const delivery =
|
|
845
|
+
const delivery = Object.keys(requestedRuntimeDelivery).length > 0
|
|
835
846
|
? { ...(currentMcp.runtime?.delivery ?? {}), ...requestedRuntimeDelivery }
|
|
836
847
|
: currentMcp.runtime?.delivery;
|
|
837
848
|
mcpSyncOverride = {
|
|
@@ -842,7 +853,7 @@ export async function collectOnboardPlan(opts = {}) {
|
|
|
842
853
|
if (!opts.dryRun && requestedRoutes && Object.keys(requestedRoutes).length > 0) {
|
|
843
854
|
writeMcpRoutesYaml(join(homeDir, ".trellis", "mcp", "servers.yaml"), routes ?? {}, backupSession);
|
|
844
855
|
}
|
|
845
|
-
if (!opts.dryRun &&
|
|
856
|
+
if (!opts.dryRun && Object.keys(requestedRuntimeDelivery).length > 0) {
|
|
846
857
|
writeMcpRuntimeDeliveryYaml(join(homeDir, ".trellis", "mcp", "servers.yaml"), delivery ?? {}, backupSession);
|
|
847
858
|
}
|
|
848
859
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* already hold themselves to for every other kind of conflict.
|
|
9
9
|
*/
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
|
-
import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { cpSync, existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { readlink, rm, symlink } from "node:fs/promises";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { join } from "node:path";
|
|
@@ -99,6 +99,23 @@ async function planOperation(homeDir, runId, op) {
|
|
|
99
99
|
}
|
|
100
100
|
return { action: "restore", path: op.path, description: `recreate ${op.path} -> ${op.beforeLinkTarget}` };
|
|
101
101
|
}
|
|
102
|
+
// Directory conflict detection is existence-only, not a deep content
|
|
103
|
+
// hash (see BackupOperation's "dir-create"/"dir-remove" doc comment
|
|
104
|
+
// in backup.ts) — a real, documented simplification, not an
|
|
105
|
+
// oversight: something touching a file inside an otherwise-untouched
|
|
106
|
+
// directory won't be caught as a conflict.
|
|
107
|
+
case "dir-create": {
|
|
108
|
+
if (!existsSync(op.path)) {
|
|
109
|
+
return { action: "already-reverted", path: op.path, description: `${op.path} no longer exists — nothing to undo` };
|
|
110
|
+
}
|
|
111
|
+
return { action: "restore", path: op.path, description: `delete ${op.path} (created by this run)` };
|
|
112
|
+
}
|
|
113
|
+
case "dir-remove": {
|
|
114
|
+
if (existsSync(op.path)) {
|
|
115
|
+
return { action: "conflict", path: op.path, description: `${op.path} exists again since this run — left untouched` };
|
|
116
|
+
}
|
|
117
|
+
return { action: "restore", path: op.path, description: `recreate ${op.path} from its content before this run (${join(runDir, op.beforeDir)})` };
|
|
118
|
+
}
|
|
102
119
|
}
|
|
103
120
|
}
|
|
104
121
|
export async function collectRollbackPlan(homeDirInput, runIdInput) {
|
|
@@ -130,6 +147,12 @@ async function restoreOperation(homeDir, runId, op) {
|
|
|
130
147
|
case "symlink-remove":
|
|
131
148
|
await symlink(op.beforeLinkTarget, op.path);
|
|
132
149
|
return;
|
|
150
|
+
case "dir-create":
|
|
151
|
+
rmSync(op.path, { recursive: true, force: true });
|
|
152
|
+
return;
|
|
153
|
+
case "dir-remove":
|
|
154
|
+
cpSync(join(runDir, op.beforeDir), op.path, { recursive: true });
|
|
155
|
+
return;
|
|
133
156
|
}
|
|
134
157
|
}
|
|
135
158
|
export async function applyRollbackPlan(homeDir, runId, manifest, items) {
|