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
@@ -6,7 +6,31 @@
6
6
  * handling, color, wrapping, cursor cleanup, and cancellation; the injected
7
7
  * streams keep the prompt loop testable without a real terminal.
8
8
  */
9
- import { CANCEL_SYMBOL, autocompleteMultiselect, isCancel, multiselect, select } from "@clack/prompts";
9
+ /**
10
+ * `@clack/prompts` (and its `@clack/core` dependency) is loaded lazily,
11
+ * inside each picker function below, rather than via a top-level
12
+ * `import` — deliberately, not an oversight. A top-level import runs the
13
+ * moment ANY module that transitively imports this file is evaluated,
14
+ * which for `trellis-gui`'s sidecar means every process start, since
15
+ * `onboard.ts` (imported unconditionally by the sidecar's `/plan/onboard`
16
+ * route) imports these functions even though the sidecar's `json: true`
17
+ * contract (see `planApply.ts`'s `OnboardWizardInput` doc comment)
18
+ * guarantees they are never actually called. Compiling the sidecar to a
19
+ * single-file binary (tasks.md 5.1) surfaced a real, current limitation
20
+ * in `@yao-pkg/pkg`'s ESM support: `@clack/prompts`' `exports`-map
21
+ * `.mjs` entry fails to resolve inside pkg's snapshot filesystem
22
+ * (`Cannot find module '.../@clack/prompts/dist/index.mjs'`), which
23
+ * previously crashed the packaged sidecar at startup even though its own
24
+ * code never uses the interactive picker at all. A dynamic `import()`
25
+ * only resolves at the point it actually runs, so the sidecar (whose
26
+ * `!opts.json` gates keep every real call site below unreached) never
27
+ * triggers that resolution attempt — while a real, interactive CLI run
28
+ * (`tsx`/`node`, not a pkg snapshot) resolves the dynamic import exactly
29
+ * as it would a static one, so this is behavior-preserving for the CLI.
30
+ */
31
+ async function clack() {
32
+ return import("@clack/prompts");
33
+ }
10
34
  function defaultStreams() {
11
35
  return { input: process.stdin, output: process.stderr };
12
36
  }
@@ -29,14 +53,15 @@ export function toggled(checked, index) {
29
53
  next[index] = !next[index];
30
54
  return next;
31
55
  }
32
- function wasCancelled(value) {
33
- return value === CANCEL_SYMBOL || isCancel(value);
56
+ function wasCancelled(value, cancelSymbol, isCancelFn) {
57
+ return value === cancelSymbol || isCancelFn(value);
34
58
  }
35
59
  /**
36
60
  * Single-select prompt. The returned number is the original item index, so
37
61
  * callers do not depend on display labels or prompt-library values.
38
62
  */
39
63
  export async function runSingleSelectPicker(items, streams = defaultStreams(), message = "请选择一个迁移来源") {
64
+ const { select, CANCEL_SYMBOL, isCancel } = await clack();
40
65
  const result = await select({
41
66
  message,
42
67
  options: items.map((item, value) => ({
@@ -48,13 +73,14 @@ export async function runSingleSelectPicker(items, streams = defaultStreams(), m
48
73
  output: streams.output,
49
74
  showInstructions: true,
50
75
  });
51
- return wasCancelled(result) ? null : result;
76
+ return wasCancelled(result, CANCEL_SYMBOL, isCancel) ? null : result;
52
77
  }
53
78
  /**
54
79
  * Checkbox prompt. The returned indices preserve the existing onboard
55
80
  * contract, while Clack handles checked state, wrapping, colors, and cleanup.
56
81
  */
57
82
  export async function runMultiSelectPicker(items, initiallyChecked, streams = defaultStreams(), message = "请选择要启用的项目") {
83
+ const { multiselect, CANCEL_SYMBOL, isCancel } = await clack();
58
84
  const result = await multiselect({
59
85
  message,
60
86
  options: items.map((label, value) => ({ label, value })),
@@ -64,12 +90,13 @@ export async function runMultiSelectPicker(items, initiallyChecked, streams = de
64
90
  required: false,
65
91
  showInstructions: true,
66
92
  });
67
- return wasCancelled(result) ? null : [...result].sort((left, right) => left - right);
93
+ return wasCancelled(result, CANCEL_SYMBOL, isCancel) ? null : [...result].sort((left, right) => left - right);
68
94
  }
69
95
  /** Searchable multiselect for large skill/MCP inventories. It returns the
70
96
  * selected labels as stable names, preserving the semantic adapter contract
71
97
  * while avoiding a wall of dozens of rows in the onboarding terminal. */
72
98
  export async function runSearchMultiSelectPicker(items, streams = defaultStreams(), message = "选择要迁移的项目") {
99
+ const { autocompleteMultiselect, CANCEL_SYMBOL, isCancel } = await clack();
73
100
  const result = await autocompleteMultiselect({
74
101
  message,
75
102
  placeholder: "输入关键词筛选",
@@ -79,5 +106,5 @@ export async function runSearchMultiSelectPicker(items, streams = defaultStreams
79
106
  output: streams.output,
80
107
  required: false,
81
108
  });
82
- return wasCancelled(result) ? null : result;
109
+ return wasCancelled(result, CANCEL_SYMBOL, isCancel) ? null : result;
83
110
  }
@@ -0,0 +1,17 @@
1
+ /** Ownership record for the two ZCode native-Skill controls Trellis changes
2
+ * during strict Runtime delivery. MCP ownership stays in mcpOwnership.ts;
3
+ * this separate record lets a later native/both delivery restore the user's
4
+ * prior values without treating an ordinary config key as an MCP server. */
5
+ export interface ZcodeSkillControlValues {
6
+ featuresSkill: boolean | null;
7
+ skillsEnabled: boolean | null;
8
+ }
9
+ export interface ZcodeSettingsOwnership {
10
+ configPath: string;
11
+ managed: ZcodeSkillControlValues;
12
+ previous: ZcodeSkillControlValues;
13
+ }
14
+ export declare function zcodeSettingsOwnershipPath(homeDir: string): string;
15
+ export declare function loadZcodeSettingsOwnership(homeDir: string): ZcodeSettingsOwnership | undefined;
16
+ export declare function saveZcodeSettingsOwnership(homeDir: string, value: ZcodeSettingsOwnership): void;
17
+ export declare function forgetZcodeSettingsOwnership(homeDir: string): void;
@@ -0,0 +1,37 @@
1
+ /** Ownership record for the two ZCode native-Skill controls Trellis changes
2
+ * during strict Runtime delivery. MCP ownership stays in mcpOwnership.ts;
3
+ * this separate record lets a later native/both delivery restore the user's
4
+ * prior values without treating an ordinary config key as an MCP server. */
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ export function zcodeSettingsOwnershipPath(homeDir) {
8
+ return join(homeDir, ".trellis", "zcode", "settings-ownership.json");
9
+ }
10
+ export function loadZcodeSettingsOwnership(homeDir) {
11
+ const path = zcodeSettingsOwnershipPath(homeDir);
12
+ if (!existsSync(path))
13
+ return undefined;
14
+ try {
15
+ const value = JSON.parse(readFileSync(path, "utf8"));
16
+ if (typeof value.configPath !== "string"
17
+ || typeof value.managed?.featuresSkill !== "boolean"
18
+ || typeof value.managed?.skillsEnabled !== "boolean"
19
+ || (value.previous?.featuresSkill !== null && typeof value.previous?.featuresSkill !== "boolean")
20
+ || (value.previous?.skillsEnabled !== null && typeof value.previous?.skillsEnabled !== "boolean"))
21
+ return undefined;
22
+ return value;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ export function saveZcodeSettingsOwnership(homeDir, value) {
29
+ const path = zcodeSettingsOwnershipPath(homeDir);
30
+ mkdirSync(join(homeDir, ".trellis", "zcode"), { recursive: true });
31
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
32
+ }
33
+ export function forgetZcodeSettingsOwnership(homeDir) {
34
+ const path = zcodeSettingsOwnershipPath(homeDir);
35
+ if (existsSync(path))
36
+ writeFileSync(path, "");
37
+ }
@@ -15150,7 +15150,8 @@ var ALL_AGENTS = [
15150
15150
  "codex",
15151
15151
  "kiro",
15152
15152
  "pi",
15153
- "kimi-code"
15153
+ "kimi-code",
15154
+ "zcode"
15154
15155
  ];
15155
15156
  function resolveScope(scope, managedAgents) {
15156
15157
  const candidates = scope ?? managedAgents;
@@ -21,4 +21,11 @@ export interface ProbeOptions {
21
21
  * default — see docs/architecture.md "MCP handshake probing is opt-in". */
22
22
  probeMcp?: boolean;
23
23
  }
24
+ /**
25
+ * Codex resolves its config through HOME, but Volta's shim resolves its own
26
+ * installation through VOLTA_HOME. A probe against a scratch HOME must keep
27
+ * those identities separate or `codex mcp list` fails before it can read the
28
+ * scratch config.
29
+ */
30
+ export declare function codexEnv(homeDir: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
24
31
  export declare function probe(homeDir?: string, opts?: ProbeOptions): Promise<AgentSnapshot>;
@@ -11,7 +11,7 @@
11
11
  import { execFileSync } from "node:child_process";
12
12
  import { existsSync, readFileSync } from "node:fs";
13
13
  import { homedir } from "node:os";
14
- import { join } from "node:path";
14
+ import { delimiter, join } from "node:path";
15
15
  import { probeMcpServer } from "../lib/mcpProbe.js";
16
16
  import { pathRef, resolveEnvRefs, scanSkillRoot } from "../lib/probeCommon.js";
17
17
  /**
@@ -30,6 +30,30 @@ export function readInstructionsPath(configTomlPath) {
30
30
  }
31
31
  return /^\s*instructions\s*=\s*"([^"]*)"/m.exec(content)?.[1];
32
32
  }
33
+ /**
34
+ * Codex resolves its config through HOME, but Volta's shim resolves its own
35
+ * installation through VOLTA_HOME. A probe against a scratch HOME must keep
36
+ * those identities separate or `codex mcp list` fails before it can read the
37
+ * scratch config.
38
+ */
39
+ export function codexEnv(homeDir, env = process.env) {
40
+ const voltaHome = env.VOLTA_HOME ?? join(homedir(), ".volta");
41
+ const voltaBin = join(voltaHome, "bin");
42
+ const pathParts = (env.PATH ?? "").split(delimiter).filter(Boolean);
43
+ const hasVolta = existsSync(voltaBin);
44
+ if (hasVolta && !pathParts.includes(voltaBin))
45
+ pathParts.unshift(voltaBin);
46
+ return {
47
+ ...env,
48
+ HOME: homeDir,
49
+ // A host may inject CODEX_HOME for its own relay/session state. It has
50
+ // higher priority than HOME inside Codex, so override it as well or a
51
+ // scratch-home probe silently reads the host's real configuration.
52
+ CODEX_HOME: join(homeDir, ".codex"),
53
+ ...(hasVolta ? { VOLTA_HOME: voltaHome } : {}),
54
+ ...(pathParts.length ? { PATH: pathParts.join(delimiter) } : {}),
55
+ };
56
+ }
33
57
  export async function probe(homeDir = homedir(), opts = {}) {
34
58
  const configTomlPath = join(homeDir, ".codex", "config.toml");
35
59
  if (!existsSync(configTomlPath)) {
@@ -43,21 +67,29 @@ export async function probe(homeDir = homedir(), opts = {}) {
43
67
  // default `homeDir` would silently get this real machine's real MCP
44
68
  // server list instead (trellis-migrate-mcp-servers found this gap in
45
69
  // src/lib/mcpMigrateRead.ts's own equivalent call; fixed there first).
46
- const codexEnv = { ...process.env, HOME: homeDir };
70
+ const scopedCodexEnv = codexEnv(homeDir);
47
71
  let version;
48
72
  try {
49
- version = execFileSync("codex", ["--version"], { encoding: "utf-8", timeout: 5_000, env: codexEnv }).trim();
73
+ version = execFileSync("codex", ["--version"], { encoding: "utf-8", timeout: 5_000, env: scopedCodexEnv }).trim();
50
74
  }
51
75
  catch {
52
76
  // config exists without the binary on PATH — unusual, still probeable
53
77
  }
54
78
  let mcpEntries = [];
55
79
  try {
56
- const raw = execFileSync("codex", ["mcp", "list", "--json"], { encoding: "utf-8", timeout: 5_000, env: codexEnv });
80
+ const raw = execFileSync("codex", ["mcp", "list", "--json"], { encoding: "utf-8", timeout: 5_000, env: scopedCodexEnv });
57
81
  mcpEntries = JSON.parse(raw);
58
82
  }
59
83
  catch (err) {
60
- diagnostics.push(`codex mcp list --json failed: ${err instanceof Error ? err.message : String(err)}`);
84
+ // ENOENT means the binary itself isn't on PATH — the same benign,
85
+ // config-without-binary condition the `--version` check above already
86
+ // swallows silently, not a real failure worth alarming the user with a
87
+ // raw spawn error string. Anything else (malformed JSON, a non-zero
88
+ // exit for another reason, a timeout) is a genuine, unexpected failure
89
+ // and stays a diagnostic.
90
+ if (err?.code !== "ENOENT") {
91
+ diagnostics.push(`codex mcp list --json failed: ${err instanceof Error ? err.message : String(err)}`);
92
+ }
61
93
  }
62
94
  const mcpServers = mcpEntries.map((entry) => ({
63
95
  name: entry.name,
@@ -0,0 +1,32 @@
1
+ /**
2
+ * ZCode probe and profile selection.
3
+ *
4
+ * ZCode has two public configuration layouts in circulation. The official
5
+ * source/distribution reads ~/.zcode/cli/config.json, while the installed
6
+ * zcode-app-cli compatibility distribution keeps runtime settings (including
7
+ * mcp.servers) in ~/.zcode/cli/setting.json. We select one profile before any
8
+ * adapter plans a write; writing both would make ZCode's native precedence
9
+ * ambiguous and can hide a managed MCP entry.
10
+ */
11
+ import type { AgentSnapshot } from "../core/types.js";
12
+ export type ZcodeProfileKind = "community-cli" | "official" | "configuration-only";
13
+ export interface ZcodeProfile {
14
+ kind: ZcodeProfileKind;
15
+ configPath: string;
16
+ executable?: string;
17
+ executableVersion?: string;
18
+ supportsExecution: boolean;
19
+ }
20
+ export interface ProbeOptions {
21
+ probeMcp?: boolean;
22
+ env?: NodeJS.ProcessEnv;
23
+ }
24
+ export declare function zcodeEnv(homeDir: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
25
+ export declare function selectZcodeProfile(homeDir: string, facts?: {
26
+ command?: string;
27
+ version?: string;
28
+ help?: string;
29
+ outputFormatSupported?: boolean;
30
+ }): ZcodeProfile | undefined;
31
+ export declare function resolveZcodeProfile(homeDir?: string, env?: NodeJS.ProcessEnv): ZcodeProfile | undefined;
32
+ export declare function probe(homeDir?: string, opts?: ProbeOptions): Promise<AgentSnapshot>;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * ZCode probe and profile selection.
3
+ *
4
+ * ZCode has two public configuration layouts in circulation. The official
5
+ * source/distribution reads ~/.zcode/cli/config.json, while the installed
6
+ * zcode-app-cli compatibility distribution keeps runtime settings (including
7
+ * mcp.servers) in ~/.zcode/cli/setting.json. We select one profile before any
8
+ * adapter plans a write; writing both would make ZCode's native precedence
9
+ * ambiguous and can hide a managed MCP entry.
10
+ */
11
+ import { execFileSync } from "node:child_process";
12
+ import { existsSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import { delimiter, join } from "node:path";
15
+ import { probeMcpServer } from "../lib/mcpProbe.js";
16
+ import { pathRef, readJsonFile, resolveEnvRefs, scanSkillRoot } from "../lib/probeCommon.js";
17
+ export function zcodeEnv(homeDir, env = process.env) {
18
+ // A Node host can inherit Volta's selected Node binary without inheriting
19
+ // Volta's shim directory in PATH. Keep the user's explicit PATH order, but
20
+ // add the documented Volta bin location when necessary so `zcode` resolves
21
+ // the same way it does in their interactive terminal.
22
+ const voltaBin = env.VOLTA_HOME ? join(env.VOLTA_HOME, "bin") : undefined;
23
+ const pathParts = (env.PATH ?? "").split(delimiter).filter(Boolean);
24
+ if (voltaBin && !pathParts.includes(voltaBin))
25
+ pathParts.unshift(voltaBin);
26
+ return { ...env, HOME: homeDir, ...(pathParts.length ? { PATH: pathParts.join(delimiter) } : {}) };
27
+ }
28
+ function configPaths(homeDir) {
29
+ const root = join(homeDir, ".zcode");
30
+ return {
31
+ root,
32
+ community: join(root, "cli", "setting.json"),
33
+ official: join(root, "cli", "config.json"),
34
+ };
35
+ }
36
+ function publicCommand(env) {
37
+ return env.TRELLIS_ZCODE_BIN?.trim() || "zcode";
38
+ }
39
+ function cliFacts(homeDir, env) {
40
+ const command = publicCommand(env);
41
+ const childEnv = zcodeEnv(homeDir, env);
42
+ try {
43
+ const version = execFileSync(command, ["version"], { encoding: "utf-8", timeout: 5_000, env: childEnv }).trim();
44
+ let help;
45
+ let outputFormatSupported = false;
46
+ try {
47
+ help = execFileSync(command, ["--help"], { encoding: "utf-8", timeout: 5_000, env: childEnv });
48
+ }
49
+ catch {
50
+ // A version identity still permits a configuration-only profile.
51
+ }
52
+ try {
53
+ // Some zcode-app-cli builds accept --output-format but omit it from
54
+ // their generated help text. --version never starts a model turn, so
55
+ // it is a safe capability probe for the public argument contract.
56
+ execFileSync(command, ["--output-format", "stream-json", "--version"], { encoding: "utf-8", timeout: 5_000, env: childEnv });
57
+ outputFormatSupported = true;
58
+ }
59
+ catch {
60
+ // Keep the false value; callers then leave execution disabled.
61
+ }
62
+ return { command, version, ...(help ? { help } : {}), outputFormatSupported };
63
+ }
64
+ catch {
65
+ return {};
66
+ }
67
+ }
68
+ export function selectZcodeProfile(homeDir, facts = {}) {
69
+ const paths = configPaths(homeDir);
70
+ const version = facts.version ?? "";
71
+ const supportsExecution = Boolean(facts.command
72
+ && facts.help?.includes("--prompt")
73
+ && facts.help.includes("--resume")
74
+ && (facts.help.includes("--output-format") || facts.outputFormatSupported === true));
75
+ if (/^zcode-app-cli\b/im.test(version) || (existsSync(paths.community) && !existsSync(paths.official))) {
76
+ return {
77
+ kind: "community-cli",
78
+ configPath: paths.community,
79
+ ...(facts.command ? { executable: facts.command } : {}),
80
+ ...(facts.version ? { executableVersion: facts.version } : {}),
81
+ supportsExecution,
82
+ };
83
+ }
84
+ if (facts.command || existsSync(paths.official)) {
85
+ return {
86
+ kind: facts.command ? "official" : "configuration-only",
87
+ configPath: paths.official,
88
+ ...(facts.command ? { executable: facts.command } : {}),
89
+ ...(facts.version ? { executableVersion: facts.version } : {}),
90
+ supportsExecution,
91
+ };
92
+ }
93
+ if (existsSync(paths.root)) {
94
+ return { kind: "configuration-only", configPath: paths.official, supportsExecution: false };
95
+ }
96
+ return undefined;
97
+ }
98
+ export function resolveZcodeProfile(homeDir = homedir(), env = process.env) {
99
+ // A public `zcode` binary is installed system-wide, whereas probes run
100
+ // against an explicit home directory. Do not let the real machine's CLI
101
+ // make an empty sandbox home appear to have ZCode installed; the profile
102
+ // must have local `.zcode` state before any executable is queried.
103
+ if (!existsSync(configPaths(homeDir).root))
104
+ return undefined;
105
+ return selectZcodeProfile(homeDir, cliFacts(homeDir, env));
106
+ }
107
+ export async function probe(homeDir = homedir(), opts = {}) {
108
+ const env = opts.env ?? process.env;
109
+ const profile = resolveZcodeProfile(homeDir, env);
110
+ if (!profile)
111
+ return { agent: "zcode", present: false, skillRoots: [], mcpServers: [], diagnostics: [] };
112
+ const config = readJsonFile(profile.configPath);
113
+ const mcpServers = Object.entries(config?.mcp?.servers ?? {}).map(([name, def]) => ({
114
+ name,
115
+ transport: def.url ? (def.type === "sse" ? "sse" : "http") : "stdio",
116
+ }));
117
+ if (opts.probeMcp) {
118
+ await Promise.all(Object.entries(config?.mcp?.servers ?? {}).map(async ([name, def], index) => {
119
+ if (def.url || !def.command)
120
+ return;
121
+ mcpServers[index].probe = await probeMcpServer({ transport: "stdio", command: def.command, args: def.args }, resolveEnvRefs(def.env, zcodeEnv(homeDir, env)));
122
+ }));
123
+ }
124
+ const skillRoots = [
125
+ scanSkillRoot(join(homeDir, ".zcode", "skills")),
126
+ scanSkillRoot(join(homeDir, ".agents", "skills")),
127
+ ].filter((root) => root !== undefined);
128
+ const diagnostics = [];
129
+ if (!profile.supportsExecution)
130
+ diagnostics.push("ZCode public CLI execution is unavailable; configuration-only management is supported");
131
+ return {
132
+ agent: "zcode",
133
+ present: true,
134
+ ...(profile.executableVersion ? { version: profile.executableVersion } : {}),
135
+ skillRoots,
136
+ mcpServers,
137
+ instructionsFile: pathRef(join(homeDir, ".zcode", "AGENTS.md")),
138
+ diagnostics,
139
+ };
140
+ }
@@ -18,7 +18,7 @@ required reading for that.
18
18
  │ trellis sync / trellis doctor
19
19
  ┌───────┼────────┬─────────────┬──────────────┐
20
20
  ▼ ▼ ▼ ▼
21
- Claude Code Codex Kiro pi Kimi Code
21
+ Claude Code Codex Kiro pi Kimi Code ZCode
22
22
  (symlink adapter) (incremental (symlink adapter) (bridge
23
23
  TOML writer) extension)
24
24
  ```
@@ -57,7 +57,7 @@ Aligned to the `.agents Protocol` draft, extended where the draft is silent:
57
57
  ## Global vs. workspace scope
58
58
 
59
59
  **Global only, for now.** Trellis manages `~/.trellis` — one canonical
60
- source per machine, applied to that machine's five supported agents. It does not yet
60
+ source per machine, applied to that machine's six supported agents. It does not yet
61
61
  read or merge a project-local `.trellis/` in a specific repo.
62
62
 
63
63
  The `.agents Protocol` draft (see `research.md`) describes a two-layer
@@ -72,7 +72,7 @@ that's scope creep against this section, not a natural extension.
72
72
 
73
73
  ## Private / agent-specific capabilities
74
74
 
75
- Not everything belongs to all five agents. A skill built around Claude
75
+ Not everything belongs to all six agents. A skill built around Claude
76
76
  Code's Task-based subagent delegation has no equivalent to delegate to on
77
77
  Codex; an MCP server might only make sense for one agent's workflow. Every
78
78
  scopable item — skill, subagent profile, memory entry, MCP server —
@@ -483,8 +483,12 @@ portable Skill contract.
483
483
  provides — `~/.config/agent-env/secrets.env`, 1Password's `op run`,
484
484
  anything that populates `process.env` before an adapter's generated
485
485
  command runs)
486
- - A GUI (P5 in the roadmap evaluates embedding into an existing one —
487
- mcp-router's or skills-hub's — before building a new one)
486
+ - A GUI is now `@trellis/gui` — P10 in the roadmap, not P5 (P5 is
487
+ `@trellis/sdk`, the read-only API the GUI's own read views could have
488
+ used but didn't, per `openspec/changes/trellis-gui/design.md` Decision
489
+ 1 — the GUI's sidecar imports internal command modules directly
490
+ instead, to reuse write/backup mechanics `@trellis/sdk`'s read-only
491
+ barrel deliberately excludes)
488
492
 
489
493
  ## MCP handshake probing is opt-in, not default
490
494
 
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-labelledby="title desc">
2
+ <title id="title">Trellis mark</title>
3
+ <desc id="desc">A letter T whose beam is a trellis lattice.</desc>
4
+ <g fill="none" stroke="#161c24" stroke-linecap="round">
5
+ <rect x="16" y="18" width="64" height="18" rx="9" stroke-width="5.5"/>
6
+ <g stroke-width="2.6" clip-path="url(#a-beam)"><path d="M-64.5 32 L-55.5 23"/><path d="M-64.5 23 L-55.5 32"/><path d="M-46.5 32 L-37.5 23"/><path d="M-46.5 23 L-37.5 32"/><path d="M-28.5 32 L-19.5 23"/><path d="M-28.5 23 L-19.5 32"/><path d="M-10.5 32 L-1.5 23"/><path d="M-10.5 23 L-1.5 32"/><path d="M7.5 32 L16.5 23"/><path d="M7.5 23 L16.5 32"/><path d="M25.5 32 L34.5 23"/><path d="M25.5 23 L34.5 32"/><path d="M43.5 32 L52.5 23"/><path d="M43.5 23 L52.5 32"/><path d="M61.5 32 L70.5 23"/><path d="M61.5 23 L70.5 32"/><path d="M79.5 32 L88.5 23"/><path d="M79.5 23 L88.5 32"/><path d="M97.5 32 L106.5 23"/><path d="M97.5 23 L106.5 32"/><path d="M115.5 32 L124.5 23"/><path d="M115.5 23 L124.5 32"/></g>
7
+ <path d="M48 36 V78" stroke-width="8"/>
8
+ </g>
9
+ <defs><clipPath id="a-beam"><rect x="21" y="23" width="54" height="9" rx="4.5"/></clipPath></defs>
10
+ </svg>
@@ -0,0 +1,60 @@
1
+ # Agent Trellis poster series
2
+
3
+ The canonical brand mark and social poster series. The earlier v1 and v2
4
+ iterations were retired and removed; they remain in git history (`f4e5136`,
5
+ `1045bb6`).
6
+
7
+ Design notes:
8
+
9
+ - The hero zone of each poster is a purpose-built diagram (agent paths,
10
+ migration flow, shared memory rail, context handoff) instead of the logo
11
+ scaled up inside empty corner brackets.
12
+ - Each theme gets its own quiet accent color (indigo / teal / violet /
13
+ orange); the mark itself stays single-ink.
14
+ - The kicker/title overlap in the v1 English layout is fixed; all metrics are
15
+ computed from the title baseline.
16
+ - One horizontal 1200x675 master (X/Twitter, WeChat article header) joins the
17
+ 3:4 verticals, in both English and Chinese.
18
+ - Vertical posters are phone-first (feed width ~390 css px): body text runs at
19
+ 34/35 px, diagram labels at 16-17 px, install line at 32 px. The horizontal
20
+ masters are desktop-first and keep the denser metrics.
21
+
22
+ ## Mark
23
+
24
+ The brand mark is the **Lattice T** — the letter T whose beam is a trellis
25
+ lattice: [svg](../brand-v3/trellis-mark-v3-a.svg) /
26
+ [png](../brand-v3/trellis-mark-v3-a.png). It is a single-ink symbol and is the
27
+ same mark used in the README header and every poster. Two retired alternates
28
+ (constellation T, lattice panel) remain in git history.
29
+
30
+ ## Posters
31
+
32
+ Vertical masters are 1080x1440 (3:4, Xiaohongshu / WeChat Moments); horizontal
33
+ masters are 1200x675 (16:9). SVG sources are the editable masters.
34
+
35
+ | Theme | EN | ZH |
36
+ |---|---|---|
37
+ | Unified runtime | [svg](agent-trellis-poster-v3-unified-en.svg) / [png](agent-trellis-poster-v3-unified-en.png) | [svg](agent-trellis-poster-v3-unified-zh.svg) / [png](agent-trellis-poster-v3-unified-zh.png) |
38
+ | One-command migration | [svg](agent-trellis-poster-v3-migrate-en.svg) / [png](agent-trellis-poster-v3-migrate-en.png) | [svg](agent-trellis-poster-v3-migrate-zh.svg) / [png](agent-trellis-poster-v3-migrate-zh.png) |
39
+ | Shared memory | [svg](agent-trellis-poster-v3-memory-en.svg) / [png](agent-trellis-poster-v3-memory-en.png) | [svg](agent-trellis-poster-v3-memory-zh.svg) / [png](agent-trellis-poster-v3-memory-zh.png) |
40
+ | Switch agents | [svg](agent-trellis-poster-v3-switch-en.svg) / [png](agent-trellis-poster-v3-switch-en.png) | [svg](agent-trellis-poster-v3-switch-zh.svg) / [png](agent-trellis-poster-v3-switch-zh.png) |
41
+ | Horizontal hero | [svg](agent-trellis-poster-v3-horizontal-en.svg) / [png](agent-trellis-poster-v3-horizontal-en.png) | [svg](agent-trellis-poster-v3-horizontal-zh.svg) / [png](agent-trellis-poster-v3-horizontal-zh.png) |
42
+
43
+ ## Copy
44
+
45
+ Same approved copy as v1:
46
+
47
+ - `One Runtime. Every Code Agent.` / 一个 Runtime,统一所有 Code Agent。
48
+ - `Migrate in one command.` / 一条命令,迁移现有配置。
49
+ - `Keep context in the loop.` / 让上下文始终在线。
50
+ - `Change Agents. Keep moving.` / 切换 Agent,不丢上下文。
51
+
52
+ ## Regenerate
53
+
54
+ ```bash
55
+ node scripts/generate-brand-v3.mjs # brand mark
56
+ node scripts/generate-poster-series-v3.mjs # 8 vertical + 2 horizontal posters
57
+ ```
58
+
59
+ Both scripts write SVG masters into `docs/assets/` and render PNGs through
60
+ `qlmanage` + `sips` (macOS built-ins, no extra dependencies).
@@ -0,0 +1,51 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1600" viewBox="0 0 1600 1600">
2
+ <rect width="1600" height="1600" fill="#fafaf8"/>
3
+ <g transform="translate(0 350)">
4
+ <g transform="translate(64 100) scale(0.58)" fill="none" stroke="#161c24" stroke-linecap="round">
5
+ <defs><clipPath id="v3-beam-64-100"><rect x="21" y="23" width="54" height="9" rx="4.5"/></clipPath></defs>
6
+ <rect x="16" y="18" width="64" height="18" rx="9" stroke-width="5.5"/>
7
+ <g stroke-width="2.6" clip-path="url(#v3-beam-64-100)"><path d="M-64.5 32 L-55.5 23"/><path d="M-64.5 23 L-55.5 32"/><path d="M-46.5 32 L-37.5 23"/><path d="M-46.5 23 L-37.5 32"/><path d="M-28.5 32 L-19.5 23"/><path d="M-28.5 23 L-19.5 32"/><path d="M-10.5 32 L-1.5 23"/><path d="M-10.5 23 L-1.5 32"/><path d="M7.5 32 L16.5 23"/><path d="M7.5 23 L16.5 32"/><path d="M25.5 32 L34.5 23"/><path d="M25.5 23 L34.5 32"/><path d="M43.5 32 L52.5 23"/><path d="M43.5 23 L52.5 32"/><path d="M61.5 32 L70.5 23"/><path d="M61.5 23 L70.5 32"/><path d="M79.5 32 L88.5 23"/><path d="M79.5 23 L88.5 32"/><path d="M97.5 32 L106.5 23"/><path d="M97.5 23 L106.5 32"/><path d="M115.5 32 L124.5 23"/><path d="M115.5 23 L124.5 32"/></g>
8
+ <path d="M48 36 V78" stroke-width="8"/>
9
+ </g>
10
+ <text x="140" y="141" text-anchor="start" font-family="Helvetica Neue, PingFang SC, Arial, sans-serif" font-size="26" font-weight="700" letter-spacing="2.6" fill="#161c24">AGENT TRELLIS</text>
11
+ <text x="1536" y="140" text-anchor="end" font-family="Menlo, SFMono-Regular, monospace" font-size="15" font-weight="400" letter-spacing="1.7" fill="#69727d">OPEN SOURCE · LOCAL FIRST</text>
12
+ <path d="M64 192 H1536" stroke="#dfe4ea" stroke-width="1.5"/>
13
+ <rect x="64" y="189" width="46" height="3" fill="#3457d5"/>
14
+
15
+ <text x="108" y="262" text-anchor="start" font-family="Menlo, SFMono-Regular, monospace" font-size="17" font-weight="400" letter-spacing="2.2" fill="#3457d5">UNIFIED CODE AGENT RUNTIME</text>
16
+ <rect x="64" y="300" width="8" height="182" fill="#3457d5"/>
17
+ <text x="108" y="366" text-anchor="start" font-family="Helvetica Neue, PingFang SC, Arial, sans-serif" font-size="84" font-weight="800" letter-spacing="-2.2" fill="#161c24">One Runtime.</text><text x="108" y="464" text-anchor="start" font-family="Helvetica Neue, PingFang SC, Arial, sans-serif" font-size="84" font-weight="800" letter-spacing="-2.2" fill="#161c24">Every Code Agent.</text>
18
+ <text x="108" y="522" text-anchor="start" font-family="Helvetica Neue, PingFang SC, Arial, sans-serif" font-size="24" font-weight="450" letter-spacing="0" fill="#69727d">Skills, MCP, Memory and Instructions</text><text x="108" y="558" text-anchor="start" font-family="Helvetica Neue, PingFang SC, Arial, sans-serif" font-size="24" font-weight="450" letter-spacing="0" fill="#69727d">stay aligned from one canonical source.</text>
19
+
20
+ <text x="108" y="640" text-anchor="start" font-family="Menlo, SFMono-Regular, monospace" font-size="15" font-weight="400" letter-spacing="1.6" fill="#69727d">SKILLS · MCP · MEMORY · INSTRUCTIONS</text>
21
+ <circle cx="112" cy="700" r="5.5" fill="#3457d5"/>
22
+ <text x="140" y="709" text-anchor="start" font-family="Menlo, SFMono-Regular, monospace" font-size="24" font-weight="500" letter-spacing="0.2" fill="#161c24">$ npm i -g agent-trellis</text>
23
+
24
+ <g>
25
+ <rect x="1000" y="250" width="500" height="450" fill="none" stroke="#dfe4ea" stroke-width="1.5"/>
26
+ <path d="M991 250 H1009 M1000 241 V259" fill="none" stroke="#dfe4ea" stroke-width="2"/><path d="M1491 250 H1509 M1500 241 V259" fill="none" stroke="#dfe4ea" stroke-width="2"/><path d="M991 700 H1009 M1000 691 V709" fill="none" stroke="#dfe4ea" stroke-width="2"/><path d="M1491 700 H1509 M1500 691 V709" fill="none" stroke="#dfe4ea" stroke-width="2"/>
27
+ <rect x="1018" y="228" width="242" height="44" fill="#fafaf8"/>
28
+ <text x="1035" y="258" text-anchor="start" font-family="Menlo, SFMono-Regular, monospace" font-size="20" font-weight="400" letter-spacing="1.8" fill="#3457d5">FIG.00 — RUNTIME</text>
29
+ </g>
30
+ <g>
31
+ <rect x="1039" y="319" width="22" height="22" rx="6" fill="none" stroke="#161c24" stroke-width="3"/><rect x="1039" y="464" width="22" height="22" rx="6" fill="none" stroke="#161c24" stroke-width="3"/><rect x="1039" y="609" width="22" height="22" rx="6" fill="none" stroke="#161c24" stroke-width="3"/>
32
+ <g fill="none">
33
+ <defs><clipPath id="d-horizontal"><rect x="1172" y="337" width="156" height="276" rx="12"/></clipPath></defs>
34
+ <rect x="1165" y="330" width="170" height="290" rx="18" stroke="#161c24" stroke-width="3.5"/>
35
+ <g clip-path="url(#d-horizontal)" stroke="#69727d" stroke-width="2" opacity="0.85"><path d="M710 613 L986 337"/><path d="M710 337 L986 613"/><path d="M731 613 L1007 337"/><path d="M731 337 L1007 613"/><path d="M752 613 L1028 337"/><path d="M752 337 L1028 613"/><path d="M773 613 L1049 337"/><path d="M773 337 L1049 613"/><path d="M794 613 L1070 337"/><path d="M794 337 L1070 613"/><path d="M815 613 L1091 337"/><path d="M815 337 L1091 613"/><path d="M836 613 L1112 337"/><path d="M836 337 L1112 613"/><path d="M857 613 L1133 337"/><path d="M857 337 L1133 613"/><path d="M878 613 L1154 337"/><path d="M878 337 L1154 613"/><path d="M899 613 L1175 337"/><path d="M899 337 L1175 613"/><path d="M920 613 L1196 337"/><path d="M920 337 L1196 613"/><path d="M941 613 L1217 337"/><path d="M941 337 L1217 613"/><path d="M962 613 L1238 337"/><path d="M962 337 L1238 613"/><path d="M983 613 L1259 337"/><path d="M983 337 L1259 613"/><path d="M1004 613 L1280 337"/><path d="M1004 337 L1280 613"/><path d="M1025 613 L1301 337"/><path d="M1025 337 L1301 613"/><path d="M1046 613 L1322 337"/><path d="M1046 337 L1322 613"/><path d="M1067 613 L1343 337"/><path d="M1067 337 L1343 613"/><path d="M1088 613 L1364 337"/><path d="M1088 337 L1364 613"/><path d="M1109 613 L1385 337"/><path d="M1109 337 L1385 613"/><path d="M1130 613 L1406 337"/><path d="M1130 337 L1406 613"/><path d="M1151 613 L1427 337"/><path d="M1151 337 L1427 613"/><path d="M1172 613 L1448 337"/><path d="M1172 337 L1448 613"/><path d="M1193 613 L1469 337"/><path d="M1193 337 L1469 613"/><path d="M1214 613 L1490 337"/><path d="M1214 337 L1490 613"/><path d="M1235 613 L1511 337"/><path d="M1235 337 L1511 613"/><path d="M1256 613 L1532 337"/><path d="M1256 337 L1532 613"/><path d="M1277 613 L1553 337"/><path d="M1277 337 L1553 613"/><path d="M1298 613 L1574 337"/><path d="M1298 337 L1574 613"/><path d="M1319 613 L1595 337"/><path d="M1319 337 L1595 613"/><path d="M1340 613 L1616 337"/><path d="M1340 337 L1616 613"/><path d="M1361 613 L1637 337"/><path d="M1361 337 L1637 613"/><path d="M1382 613 L1658 337"/><path d="M1382 337 L1658 613"/><path d="M1403 613 L1679 337"/><path d="M1403 337 L1679 613"/><path d="M1424 613 L1700 337"/><path d="M1424 337 L1700 613"/><path d="M1445 613 L1721 337"/><path d="M1445 337 L1721 613"/><path d="M1466 613 L1742 337"/><path d="M1466 337 L1742 613"/><path d="M1487 613 L1763 337"/><path d="M1487 337 L1763 613"/><path d="M1508 613 L1784 337"/><path d="M1508 337 L1784 613"/><path d="M1529 613 L1805 337"/><path d="M1529 337 L1805 613"/><path d="M1550 613 L1826 337"/><path d="M1550 337 L1826 613"/><path d="M1571 613 L1847 337"/><path d="M1571 337 L1847 613"/><path d="M1592 613 L1868 337"/><path d="M1592 337 L1868 613"/><path d="M1613 613 L1889 337"/><path d="M1613 337 L1889 613"/><path d="M1634 613 L1910 337"/><path d="M1634 337 L1910 613"/></g>
36
+ </g>
37
+ <g fill="none" stroke="#161c24" stroke-width="3">
38
+ <path d="M1062 330 C 1120 330 1128 400 1165 400"/>
39
+ <path d="M1062 475 H1165"/>
40
+ <path d="M1062 620 C 1120 620 1128 550 1165 550"/>
41
+ <path d="M1335 400 H1450 M1335 475 H1450 M1335 550 H1450"/>
42
+ </g>
43
+ <g fill="#3457d5"><circle cx="1456" cy="400" r="6.5"/><circle cx="1456" cy="475" r="6.5"/><circle cx="1456" cy="550" r="6.5"/></g>
44
+ </g>
45
+ <text x="1250" y="745" text-anchor="middle" font-family="Menlo, SFMono-Regular, monospace" font-size="14" font-weight="400" letter-spacing="2" fill="#3457d5">ONE RUNTIME · MANY AGENTS</text>
46
+
47
+ <path d="M64 784 H1536" stroke="#dfe4ea" stroke-width="1.5"/>
48
+ <text x="64" y="822" text-anchor="start" font-family="Menlo, SFMono-Regular, monospace" font-size="13.5" font-weight="400" letter-spacing="1.5" fill="#69727d">AGENT TRELLIS — UNIFIED CODE AGENT RUNTIME</text>
49
+ <text x="1536" y="822" text-anchor="end" font-family="Menlo, SFMono-Regular, monospace" font-size="13.5" font-weight="400" letter-spacing="1.5" fill="#69727d">agent-trellis.dev</text>
50
+ </g>
51
+ </svg>