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.
Files changed (251) hide show
  1. package/README.md +45 -206
  2. package/dist/adapters/zcode.d.ts +21 -0
  3. package/dist/adapters/zcode.js +234 -0
  4. package/dist/cli.js +119 -10
  5. package/dist/commands/doctor.js +2 -0
  6. package/dist/commands/init.d.ts +1 -1
  7. package/dist/commands/init.js +4 -1
  8. package/dist/commands/mcp.d.ts +56 -14
  9. package/dist/commands/mcp.js +105 -28
  10. package/dist/commands/mcpGateway.js +2 -1
  11. package/dist/commands/mcpImport.d.ts +21 -2
  12. package/dist/commands/mcpImport.js +30 -7
  13. package/dist/commands/memory.d.ts +13 -1
  14. package/dist/commands/memory.js +15 -0
  15. package/dist/commands/migrate.js +4 -1
  16. package/dist/commands/onboard.js +15 -4
  17. package/dist/commands/remoteSkill.d.ts +90 -0
  18. package/dist/commands/remoteSkill.js +392 -0
  19. package/dist/commands/rollback.js +55 -1
  20. package/dist/commands/secretsAudit.js +4 -1
  21. package/dist/commands/skill.d.ts +37 -6
  22. package/dist/commands/skill.js +73 -15
  23. package/dist/commands/sync.js +3 -1
  24. package/dist/core/adapter.d.ts +11 -1
  25. package/dist/core/canonical.d.ts +17 -1
  26. package/dist/core/canonical.js +54 -0
  27. package/dist/core/types.d.ts +1 -1
  28. package/dist/core/types.js +1 -0
  29. package/dist/lib/agentBridge.d.ts +169 -0
  30. package/dist/lib/agentBridge.js +367 -0
  31. package/dist/lib/agentBridgeProvider.d.ts +14 -0
  32. package/dist/lib/agentBridgeProvider.js +95 -0
  33. package/dist/lib/backup.d.ts +42 -0
  34. package/dist/lib/backup.js +36 -3
  35. package/dist/lib/chatAgents.d.ts +42 -0
  36. package/dist/lib/chatAgents.js +59 -0
  37. package/dist/lib/dirDigest.d.ts +11 -0
  38. package/dist/lib/dirDigest.js +52 -0
  39. package/dist/lib/envVarNames.d.ts +3 -0
  40. package/dist/lib/envVarNames.js +19 -0
  41. package/dist/lib/installAgent.d.ts +1 -1
  42. package/dist/lib/installAgent.js +1 -1
  43. package/dist/lib/mcpMigrateRead.d.ts +1 -0
  44. package/dist/lib/mcpMigrateRead.js +19 -1
  45. package/dist/lib/realHomeSnapshot.js +5 -0
  46. package/dist/lib/remoteSkillLock.d.ts +26 -0
  47. package/dist/lib/remoteSkillLock.js +88 -0
  48. package/dist/lib/remoteSkillSource.d.ts +45 -0
  49. package/dist/lib/remoteSkillSource.js +206 -0
  50. package/dist/lib/runtimeControlProvider.js +2 -0
  51. package/dist/lib/taskStore.js +1 -1
  52. package/dist/lib/terminalPicker.js +33 -6
  53. package/dist/lib/zcodeOwnership.d.ts +17 -0
  54. package/dist/lib/zcodeOwnership.js +37 -0
  55. package/dist/pi-bridge/bundle.js +2 -1
  56. package/dist/probes/codex.d.ts +7 -0
  57. package/dist/probes/codex.js +37 -5
  58. package/dist/probes/zcode.d.ts +32 -0
  59. package/dist/probes/zcode.js +140 -0
  60. package/docs/architecture.md +9 -5
  61. package/docs/assets/brand-v3/trellis-mark-v3-a.png +0 -0
  62. package/docs/assets/brand-v3/trellis-mark-v3-a.svg +10 -0
  63. package/docs/assets/poster-series-v3/README.md +60 -0
  64. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-en.png +0 -0
  65. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-en.svg +51 -0
  66. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-zh.png +0 -0
  67. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-horizontal-zh.svg +51 -0
  68. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-en.png +0 -0
  69. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-en.svg +48 -0
  70. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-zh.png +0 -0
  71. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-memory-zh.svg +48 -0
  72. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-en.png +0 -0
  73. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-en.svg +56 -0
  74. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-zh.png +0 -0
  75. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-migrate-zh.svg +56 -0
  76. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-en.png +0 -0
  77. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-en.svg +47 -0
  78. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-zh.png +0 -0
  79. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-switch-zh.svg +47 -0
  80. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-en.png +0 -0
  81. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-en.svg +51 -0
  82. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-zh.png +0 -0
  83. package/docs/assets/poster-series-v3/agent-trellis-poster-v3-unified-zh.svg +51 -0
  84. package/docs/getting-started.md +244 -14
  85. package/docs/overview.md +6 -3
  86. package/docs/overview.zh-CN.md +5 -3
  87. package/docs/roadmap.md +2 -1
  88. package/docs/video/README.md +76 -0
  89. package/docs/video/build/audio/01-opening.aiff +0 -0
  90. package/docs/video/build/audio/01-opening.mp3 +0 -0
  91. package/docs/video/build/audio/02-pain.aiff +0 -0
  92. package/docs/video/build/audio/02-pain.mp3 +0 -0
  93. package/docs/video/build/audio/03-solution.aiff +0 -0
  94. package/docs/video/build/audio/03-solution.mp3 +0 -0
  95. package/docs/video/build/audio/04-install.aiff +0 -0
  96. package/docs/video/build/audio/04-install.mp3 +0 -0
  97. package/docs/video/build/audio/04-setup.mp3 +0 -0
  98. package/docs/video/build/audio/05-dryrun.mp3 +0 -0
  99. package/docs/video/build/audio/05-init.aiff +0 -0
  100. package/docs/video/build/audio/05-init.mp3 +0 -0
  101. package/docs/video/build/audio/05-interactive.mp3 +0 -0
  102. package/docs/video/build/audio/06-dryrun.aiff +0 -0
  103. package/docs/video/build/audio/06-dryrun.mp3 +0 -0
  104. package/docs/video/build/audio/06-onboard.mp3 +0 -0
  105. package/docs/video/build/audio/06-run.mp3 +0 -0
  106. package/docs/video/build/audio/07-onboard.aiff +0 -0
  107. package/docs/video/build/audio/07-onboard.mp3 +0 -0
  108. package/docs/video/build/audio/07-verify.mp3 +0 -0
  109. package/docs/video/build/audio/08-closing.mp3 +0 -0
  110. package/docs/video/build/audio/08-doctor.aiff +0 -0
  111. package/docs/video/build/audio/08-doctor.mp3 +0 -0
  112. package/docs/video/build/audio/09-rollback.aiff +0 -0
  113. package/docs/video/build/audio/09-rollback.mp3 +0 -0
  114. package/docs/video/build/audio/10-principles.aiff +0 -0
  115. package/docs/video/build/audio/10-principles.mp3 +0 -0
  116. package/docs/video/build/audio/11-closing.aiff +0 -0
  117. package/docs/video/build/audio/11-closing.mp3 +0 -0
  118. package/docs/video/build/frames/01-opening-0.png +0 -0
  119. package/docs/video/build/frames/02-pain-1.png +0 -0
  120. package/docs/video/build/frames/02-pain-2.png +0 -0
  121. package/docs/video/build/frames/03-solution-3.png +0 -0
  122. package/docs/video/build/frames/04-install-4.png +0 -0
  123. package/docs/video/build/frames/04-setup-4.png +0 -0
  124. package/docs/video/build/frames/04-setup-5.png +0 -0
  125. package/docs/video/build/frames/05-dryrun-6.png +0 -0
  126. package/docs/video/build/frames/05-init-5.png +0 -0
  127. package/docs/video/build/frames/05-interactive-5.png +0 -0
  128. package/docs/video/build/frames/05-interactive-6.png +0 -0
  129. package/docs/video/build/frames/06-dryrun-6.png +0 -0
  130. package/docs/video/build/frames/06-dryrun-7.png +0 -0
  131. package/docs/video/build/frames/06-onboard-7.png +0 -0
  132. package/docs/video/build/frames/06-onboard-8.png +0 -0
  133. package/docs/video/build/frames/06-run-7.png +0 -0
  134. package/docs/video/build/frames/06-run-8.png +0 -0
  135. package/docs/video/build/frames/07-onboard-10.png +0 -0
  136. package/docs/video/build/frames/07-onboard-7.png +0 -0
  137. package/docs/video/build/frames/07-onboard-8.png +0 -0
  138. package/docs/video/build/frames/07-onboard-9.png +0 -0
  139. package/docs/video/build/frames/07-verify-10.png +0 -0
  140. package/docs/video/build/frames/07-verify-8.png +0 -0
  141. package/docs/video/build/frames/07-verify-9.png +0 -0
  142. package/docs/video/build/frames/08-closing-10.png +0 -0
  143. package/docs/video/build/frames/08-closing-11.png +0 -0
  144. package/docs/video/build/frames/08-doctor-11.png +0 -0
  145. package/docs/video/build/frames/08-doctor-9.png +0 -0
  146. package/docs/video/build/frames/09-rollback-10.png +0 -0
  147. package/docs/video/build/frames/09-rollback-12.png +0 -0
  148. package/docs/video/build/frames/10-principles-11.png +0 -0
  149. package/docs/video/build/frames/10-principles-13.png +0 -0
  150. package/docs/video/build/frames/11-closing-12.png +0 -0
  151. package/docs/video/build/frames/11-closing-14.png +0 -0
  152. package/docs/video/build/segments/01-opening.mp4 +0 -0
  153. package/docs/video/build/segments/01-opening.txt +3 -0
  154. package/docs/video/build/segments/02-pain.mp4 +0 -0
  155. package/docs/video/build/segments/02-pain.txt +5 -0
  156. package/docs/video/build/segments/03-solution.mp4 +0 -0
  157. package/docs/video/build/segments/03-solution.txt +3 -0
  158. package/docs/video/build/segments/04-install.mp4 +0 -0
  159. package/docs/video/build/segments/04-install.txt +3 -0
  160. package/docs/video/build/segments/04-setup.mp4 +0 -0
  161. package/docs/video/build/segments/04-setup.txt +3 -0
  162. package/docs/video/build/segments/05-dryrun.mp4 +0 -0
  163. package/docs/video/build/segments/05-dryrun.txt +3 -0
  164. package/docs/video/build/segments/05-init.mp4 +0 -0
  165. package/docs/video/build/segments/05-init.txt +3 -0
  166. package/docs/video/build/segments/05-interactive.mp4 +0 -0
  167. package/docs/video/build/segments/05-interactive.txt +5 -0
  168. package/docs/video/build/segments/06-dryrun.mp4 +0 -0
  169. package/docs/video/build/segments/06-dryrun.txt +3 -0
  170. package/docs/video/build/segments/06-onboard.mp4 +0 -0
  171. package/docs/video/build/segments/06-onboard.txt +5 -0
  172. package/docs/video/build/segments/06-run.mp4 +0 -0
  173. package/docs/video/build/segments/06-run.txt +3 -0
  174. package/docs/video/build/segments/07-onboard.mp4 +0 -0
  175. package/docs/video/build/segments/07-onboard.txt +5 -0
  176. package/docs/video/build/segments/07-verify.mp4 +0 -0
  177. package/docs/video/build/segments/07-verify.txt +5 -0
  178. package/docs/video/build/segments/08-closing.mp4 +0 -0
  179. package/docs/video/build/segments/08-closing.txt +3 -0
  180. package/docs/video/build/segments/08-doctor.mp4 +0 -0
  181. package/docs/video/build/segments/08-doctor.txt +3 -0
  182. package/docs/video/build/segments/09-rollback.mp4 +0 -0
  183. package/docs/video/build/segments/09-rollback.txt +3 -0
  184. package/docs/video/build/segments/10-principles.mp4 +0 -0
  185. package/docs/video/build/segments/10-principles.txt +3 -0
  186. package/docs/video/build/segments/11-closing.mp4 +0 -0
  187. package/docs/video/build/segments/11-closing.txt +3 -0
  188. package/docs/video/build/segments/final.txt +8 -0
  189. package/docs/video/build-ai/audio/01-hook.mp3 +0 -0
  190. package/docs/video/build-ai/audio/02-pain.mp3 +0 -0
  191. package/docs/video/build-ai/audio/03-solution.mp3 +0 -0
  192. package/docs/video/build-ai/audio/04-how.mp3 +0 -0
  193. package/docs/video/build-ai/audio/05-safe.mp3 +0 -0
  194. package/docs/video/build-ai/audio/06-closing.mp3 +0 -0
  195. package/docs/video/build-ai/clips/01-hook.mp4 +0 -0
  196. package/docs/video/build-ai/clips/02-pain.mp4 +0 -0
  197. package/docs/video/build-ai/clips/03-solution.mp4 +0 -0
  198. package/docs/video/build-ai/clips/04-how.mp4 +0 -0
  199. package/docs/video/build-ai/clips/05-safe.mp4 +0 -0
  200. package/docs/video/build-ai/clips/06-closing.mp4 +0 -0
  201. package/docs/video/build-ai/first-frame-02-pain.png +0 -0
  202. package/docs/video/build-ai/first-frame-03-solution.png +0 -0
  203. package/docs/video/build-ai/first-frame-04-how.png +0 -0
  204. package/docs/video/build-ai/first-frame-05-safe.png +0 -0
  205. package/docs/video/build-ai/segments/01-hook.caption.txt +2 -0
  206. package/docs/video/build-ai/segments/01-hook.mp4 +0 -0
  207. package/docs/video/build-ai/segments/02-pain.mp4 +0 -0
  208. package/docs/video/build-ai/segments/03-solution.mp4 +0 -0
  209. package/docs/video/build-ai/segments/04-how.mp4 +0 -0
  210. package/docs/video/build-ai/segments/05-safe.mp4 +0 -0
  211. package/docs/video/build-ai/segments/06-closing.mp4 +0 -0
  212. package/docs/video/build-ai/segments/cap-01-hook.png +0 -0
  213. package/docs/video/build-ai/segments/cap-02-pain.png +0 -0
  214. package/docs/video/build-ai/segments/cap-03-solution.png +0 -0
  215. package/docs/video/build-ai/segments/cap-04-how.png +0 -0
  216. package/docs/video/build-ai/segments/cap-05-safe.png +0 -0
  217. package/docs/video/build-ai/segments/cap-06-closing.png +0 -0
  218. package/docs/video/build-ai/segments/final.txt +6 -0
  219. package/docs/video/build-posters/audio/01-hook.mp3 +0 -0
  220. package/docs/video/build-posters/audio/02-pain.mp3 +0 -0
  221. package/docs/video/build-posters/audio/03-solution.mp3 +0 -0
  222. package/docs/video/build-posters/audio/04-how.mp3 +0 -0
  223. package/docs/video/build-posters/audio/05-safe.mp3 +0 -0
  224. package/docs/video/build-posters/audio/06-closing.mp3 +0 -0
  225. package/docs/video/build-posters/cap-01-hook.png +0 -0
  226. package/docs/video/build-posters/cap-02-pain.png +0 -0
  227. package/docs/video/build-posters/cap-03-solution.png +0 -0
  228. package/docs/video/build-posters/cap-04-how.png +0 -0
  229. package/docs/video/build-posters/cap-05-safe.png +0 -0
  230. package/docs/video/build-posters/cap-06-closing.png +0 -0
  231. package/docs/video/build-posters/card-01-hook.png +0 -0
  232. package/docs/video/build-posters/card-06-closing.png +0 -0
  233. package/docs/video/build-posters/segments/01-hook.mp4 +0 -0
  234. package/docs/video/build-posters/segments/02-pain.mp4 +0 -0
  235. package/docs/video/build-posters/segments/03-solution.mp4 +0 -0
  236. package/docs/video/build-posters/segments/04-how.mp4 +0 -0
  237. package/docs/video/build-posters/segments/05-safe.mp4 +0 -0
  238. package/docs/video/build-posters/segments/06-closing.mp4 +0 -0
  239. package/docs/video/build-posters/segments/final.txt +6 -0
  240. package/docs/video/generate-video-ai.mjs +334 -0
  241. package/docs/video/generate-video-posters.mjs +204 -0
  242. package/docs/video/generate-video.mjs +426 -0
  243. package/docs/video/outline.zh-CN.md +102 -0
  244. package/docs/video/trellis-demo-video-ai.mp4 +0 -0
  245. package/docs/video/trellis-demo-video.mp4 +0 -0
  246. package/docs/video/voiceover.zh-CN.md +49 -0
  247. package/package.json +5 -2
  248. package/schema/servers.example.yaml +1 -1
  249. package/docs/assets/agent-trellis-architecture-horizontal.svg +0 -80
  250. package/docs/assets/agent-trellis-architecture-vertical.svg +0 -82
  251. package/docs/assets/trellis-mark.svg +0 -12
@@ -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
- export function runMcpImport(path, opts = {}) {
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 internal = collectInternalPlan(path, homeDir);
320
- if (!opts.dryRun)
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
- return { exitCode: result.items.some((item) => item.action === "conflict" || item.action === "invalid-input") ? 1 : 0 };
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. */
@@ -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. */
@@ -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) {
@@ -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
- const requestedRuntimeDelivery = capabilitySelection?.runtimeDelivery;
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) || (requestedRuntimeDelivery && Object.keys(requestedRuntimeDelivery).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 = requestedRuntimeDelivery && Object.keys(requestedRuntimeDelivery).length > 0
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 && requestedRuntimeDelivery && Object.keys(requestedRuntimeDelivery).length > 0) {
856
+ if (!opts.dryRun && Object.keys(requestedRuntimeDelivery).length > 0) {
846
857
  writeMcpRuntimeDeliveryYaml(join(homeDir, ".trellis", "mcp", "servers.yaml"), delivery ?? {}, backupSession);
847
858
  }
848
859
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `trellis add` and `trellis update`: GitHub-hosted Skill import with
3
+ * Trellis-owned canonical storage, provenance, scope, backup, and sync.
4
+ */
5
+ import type { Scope } from "../core/types.js";
6
+ import { type BackupSession } from "../lib/backup.js";
7
+ import { type RemoteSkillLockEntry } from "../lib/remoteSkillLock.js";
8
+ import { type FetchedRemoteRepository } from "../lib/remoteSkillSource.js";
9
+ import type { SyncReport } from "./sync.js";
10
+ export type RemoteSkillAction = "create" | "already-present" | "conflict" | "invalid-source" | "invalid-options";
11
+ export interface RemoteSkillAddPlan {
12
+ name: string;
13
+ action: RemoteSkillAction;
14
+ detail: string;
15
+ source?: string;
16
+ requestedRef?: string;
17
+ commit?: string;
18
+ subdirectory?: string;
19
+ digest?: string;
20
+ canonicalPath?: string;
21
+ scope?: Scope;
22
+ /** Present only in process memory; never included in CLI output. */
23
+ sourceDir?: string;
24
+ }
25
+ export interface RemoteSkillAddOptions {
26
+ branch?: string;
27
+ agents?: readonly string[];
28
+ homeDir?: string;
29
+ dryRun?: boolean;
30
+ json?: boolean;
31
+ }
32
+ /** Builds an import plan from an already-fetched source. Keeping fetching
33
+ * outside this function makes the real Git transport testable with a local
34
+ * bare repository while public CLI input remains GitHub-only. */
35
+ export declare function collectRemoteSkillAddPlan(fetched: FetchedRemoteRepository, name: string | undefined, opts?: Omit<RemoteSkillAddOptions, "branch" | "dryRun" | "json">): RemoteSkillAddPlan;
36
+ export interface RemoteSkillAddOutcome {
37
+ plan: Omit<RemoteSkillAddPlan, "sourceDir">;
38
+ sync?: SyncReport;
39
+ }
40
+ export declare function applyRemoteSkillAddPlan(plan: RemoteSkillAddPlan, opts?: {
41
+ homeDir?: string;
42
+ dryRun?: boolean;
43
+ backupSession?: BackupSession;
44
+ }): Promise<RemoteSkillAddOutcome>;
45
+ /** CLI-facing remote add. The only source accepted here is normalized
46
+ * GitHub input; tests use collectRemoteSkillAddPlan with a local fetch. */
47
+ export declare function runRemoteSkillAdd(sourceInput: string | undefined, name: string | undefined, opts?: RemoteSkillAddOptions): Promise<{
48
+ exitCode: number;
49
+ }>;
50
+ export declare function runRemoteSkillList(sourceInput: string | undefined, opts?: {
51
+ branch?: string;
52
+ json?: boolean;
53
+ }): Promise<{
54
+ exitCode: number;
55
+ }>;
56
+ export type RemoteSkillUpdateAction = "already-current" | "update" | "conflict" | "not-tracked" | "invalid-source";
57
+ export interface RemoteSkillUpdatePlan {
58
+ name: string;
59
+ action: RemoteSkillUpdateAction;
60
+ detail: string;
61
+ source?: string;
62
+ requestedRef?: string;
63
+ previousCommit?: string;
64
+ commit?: string;
65
+ previousDigest?: string;
66
+ digest?: string;
67
+ canonicalPath?: string;
68
+ sourceDir?: string;
69
+ replaceDirectory?: boolean;
70
+ }
71
+ export declare function collectRemoteSkillUpdatePlan(name: string, fetched: FetchedRemoteRepository | undefined, homeDir?: string): RemoteSkillUpdatePlan;
72
+ export interface RemoteSkillUpdateOutcome {
73
+ plans: readonly Omit<RemoteSkillUpdatePlan, "sourceDir">[];
74
+ sync?: SyncReport;
75
+ }
76
+ export declare function applyRemoteSkillUpdatePlans(plans: readonly RemoteSkillUpdatePlan[], opts?: {
77
+ homeDir?: string;
78
+ dryRun?: boolean;
79
+ backupSession?: BackupSession;
80
+ }): Promise<RemoteSkillUpdateOutcome>;
81
+ export declare function runRemoteSkillUpdate(names: readonly string[], opts?: {
82
+ homeDir?: string;
83
+ dryRun?: boolean;
84
+ json?: boolean;
85
+ }): Promise<{
86
+ exitCode: number;
87
+ }>;
88
+ /** Used by Skill listing to distinguish remote provenance from local content
89
+ * without making a malformed bookkeeping file crash a read-only list. */
90
+ export declare function remoteSkillProvenance(homeDir: string): Record<string, RemoteSkillLockEntry>;