agent-trellis 0.1.0 → 0.3.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 (67) hide show
  1. package/README.md +69 -17
  2. package/dist/adapters/claude-code.d.ts +5 -3
  3. package/dist/adapters/claude-code.js +27 -14
  4. package/dist/adapters/codex.d.ts +8 -4
  5. package/dist/adapters/codex.js +47 -16
  6. package/dist/adapters/jsonMcp.d.ts +16 -5
  7. package/dist/adapters/jsonMcp.js +38 -29
  8. package/dist/adapters/kiro.d.ts +5 -3
  9. package/dist/adapters/kiro.js +29 -16
  10. package/dist/adapters/mcpPlan.d.ts +11 -6
  11. package/dist/adapters/mcpPlan.js +40 -7
  12. package/dist/adapters/pi.d.ts +2 -1
  13. package/dist/adapters/pi.js +4 -4
  14. package/dist/adapters/symlinkPlan.d.ts +7 -3
  15. package/dist/adapters/symlinkPlan.js +42 -16
  16. package/dist/cli.js +161 -18
  17. package/dist/commands/init.js +11 -0
  18. package/dist/commands/mcp.d.ts +114 -7
  19. package/dist/commands/mcp.js +258 -17
  20. package/dist/commands/memory.d.ts +39 -0
  21. package/dist/commands/memory.js +78 -0
  22. package/dist/commands/migrate.d.ts +30 -4
  23. package/dist/commands/migrate.js +83 -16
  24. package/dist/commands/onboard.d.ts +52 -7
  25. package/dist/commands/onboard.js +318 -35
  26. package/dist/commands/rollback.d.ts +44 -0
  27. package/dist/commands/rollback.js +201 -0
  28. package/dist/commands/secretsAudit.d.ts +7 -0
  29. package/dist/commands/secretsAudit.js +14 -7
  30. package/dist/commands/skill.d.ts +51 -0
  31. package/dist/commands/skill.js +104 -0
  32. package/dist/commands/sync.d.ts +13 -0
  33. package/dist/commands/sync.js +31 -5
  34. package/dist/core/adapter.d.ts +28 -11
  35. package/dist/core/adapter.js +2 -2
  36. package/dist/core/canonical.d.ts +26 -1
  37. package/dist/core/canonical.js +103 -3
  38. package/dist/core/types.d.ts +29 -1
  39. package/dist/core/types.js +11 -2
  40. package/dist/lib/backup.d.ts +56 -0
  41. package/dist/lib/backup.js +98 -0
  42. package/dist/lib/deepEqual.d.ts +8 -0
  43. package/dist/lib/deepEqual.js +26 -0
  44. package/dist/lib/dirEquals.d.ts +9 -0
  45. package/dist/lib/dirEquals.js +15 -1
  46. package/dist/lib/installAgent.d.ts +26 -0
  47. package/dist/lib/installAgent.js +46 -0
  48. package/dist/lib/mcpMigrateRead.d.ts +69 -0
  49. package/dist/lib/mcpMigrateRead.js +188 -0
  50. package/dist/lib/mcpOwnership.d.ts +25 -0
  51. package/dist/lib/mcpOwnership.js +50 -0
  52. package/dist/lib/memoryGraph.d.ts +60 -0
  53. package/dist/lib/memoryGraph.js +101 -0
  54. package/dist/lib/realHomeSnapshot.d.ts +26 -0
  55. package/dist/lib/realHomeSnapshot.js +77 -0
  56. package/dist/lib/terminalPicker.d.ts +45 -0
  57. package/dist/lib/terminalPicker.js +193 -0
  58. package/dist/lib/tomlSection.d.ts +20 -6
  59. package/dist/lib/tomlSection.js +78 -12
  60. package/dist/pi-bridge/bundle.js +100 -51
  61. package/dist/pi-bridge/index.js +14 -2
  62. package/dist/probes/codex.js +10 -2
  63. package/docs/architecture.md +7 -4
  64. package/docs/getting-started.md +267 -33
  65. package/docs/roadmap.md +444 -0
  66. package/package.json +1 -1
  67. package/schema/servers.example.yaml +39 -2
@@ -16,39 +16,62 @@ $ trellis onboard
16
16
  ```
17
17
 
18
18
  Runs `init`, detects which of Claude Code/Codex/Kiro/pi are on this machine,
19
- picks one as the migration base, then runs `migrate` and `sync` against it.
20
-
21
- - **No agent detected**: prints each agent's real install command/URL and
22
- stops. Never installs anything itself — that's your call.
23
- - **Exactly one agent detected**: auto-selected as the base, no prompt.
24
- - **Two or more detected**: prompts you to pick one (if you're at a real
25
- terminal), or pass `--agent <id>` to skip the prompt — useful in scripts,
26
- CI, or when running with `--json`, which never prompts.
19
+ then resolves two independent choices before running `migrate`, `sync`,
20
+ `mcp sync`, and `secrets audit` — the whole onboarding path, no follow-up
21
+ commands to type by hand:
22
+
23
+ 1. **Migration source** read from, at most one, never written back to.
24
+ - **No agent has real content**: skipped canonical starts from `init`'s
25
+ placeholder.
26
+ - **Exactly one agent has real content**: auto-selected, no prompt.
27
+ - **Two or more**: prompts you to choose (Up/Down or j/k, Enter to confirm,
28
+ on a real terminal that supports it — falls back to a numbered
29
+ type-a-digit prompt otherwise), or pass `--agent <id>` to skip the
30
+ prompt entirely.
31
+ 2. **Managed set** — zero or more agents to actually write to. Always an
32
+ explicit choice: pass `--manage <ids>` (comma-separated, e.g. `--manage
33
+ pi,codex`) or `--manage none`, or answer the interactive checkbox prompt
34
+ (Space to toggle, Enter to confirm — same numbered fallback as above).
35
+ **The source is not included by default** — migrating from Claude Code
36
+ doesn't mean Trellis starts managing Claude Code too, unless you say so.
37
+ Selecting an agent that isn't installed yet is itself the authorization to
38
+ install it (one confirmation, then a real `npm install -g <package>`);
39
+ Kiro has no CLI package and is refused with its download URL instead.
27
40
 
28
41
  ```
29
- $ trellis onboard --agent claude-code
30
- Using claude-code as the migration base (--agent).
42
+ $ trellis onboard --agent claude-code --manage pi
43
+ Using claude-code as the migration source (--agent).
44
+ Managed agents: pi
31
45
 
32
46
  migrate --from claude-code
33
47
  [create] skill "my-skill" — will copy from /Users/you/.claude/skills/my-skill
34
48
  ...
35
49
 
36
50
  sync
37
- codex — 1 created, 0 removed, 0 conflict(s)
51
+ pi — 1 created, 0 removed, 0 conflict(s)
38
52
  ...
39
53
 
40
- Next: `trellis mcp sync` to distribute MCP servers, `trellis secrets audit` to check for leaked credentials.
54
+ mcp sync
55
+ ✅ pi — already in sync
56
+
57
+ secrets audit
58
+ ✅ no findings — every present agent's real config and every declared env var passed all checks
41
59
  ```
42
60
 
43
- Add `--dry-run` to preview the entire chain init/migrate/sync with zero
44
- writes anywhere.
61
+ No agent named `claude-code` appears in the `sync`/`mcp sync` output above
62
+ it's present and was the migration source, but it isn't managed, so it's
63
+ never even probed as a sync target, not just left with zero items.
45
64
 
46
- **Picking a base agent only picks one.** If you use two or more agents with
47
- genuinely different real content, onboard migrates from the one you (or it)
48
- chose; the others' own differing content is untouched, exactly as `migrate`
49
- would report it if run against them directly (see the conflict table
50
- below). Merging differing content across multiple agents into one result
51
- isn't built yet see [README's Status](../README.md#status).
65
+ Add `--dry-run` to preview the entire chain init/migrate/sync/mcp
66
+ sync, including what would be written to `~/.trellis/managed.yaml` with
67
+ zero writes anywhere (secrets audit is always read-only, with or without
68
+ the flag).
69
+
70
+ **A managed agent's own real content still isn't overwritten.** If you
71
+ explicitly include the source in `--manage`, sync still never overwrites
72
+ its real files — see the conflict table below. Merging differing content
73
+ across multiple agents into one canonical result isn't built yet — see
74
+ [README's Status](../README.md#status).
52
75
 
53
76
  The rest of this page is the same flow broken into its individual steps —
54
77
  useful if you want more control over any one part, or just want to
@@ -114,32 +137,140 @@ Add `--dry-run` to see the plan without writing anything:
114
137
  $ trellis migrate --from codex --dry-run
115
138
  ```
116
139
 
140
+ Add `--only skills`, `--only instructions`, or `--only mcp` to migrate
141
+ just one category — useful when you only want part of it brought in
142
+ right now. Omit it to migrate all three, exactly as above:
143
+
144
+ ```
145
+ $ trellis migrate --from codex --only instructions
146
+ ```
147
+
117
148
  **What each action means:**
118
149
 
119
150
  | Action | Meaning |
120
151
  |---|---|
121
152
  | `create` | New to canonical source — copied in. |
122
- | `already-migrated` | Canonical already has byte-identical content (safe re-run, nothing happens). |
153
+ | `already-migrated` | Canonical already has byte-identical (or, for MCP servers, structurally identical) content (safe re-run, nothing happens). |
123
154
  | `conflict` | Canonical already has *different* real content — **left untouched**, resolve by hand. |
124
155
  | `skip-symlink` | That agent's own copy is itself a symlink (already shared in from elsewhere) — nothing of that agent's own to import. |
125
156
  | `skip-case-broken` | Found as `skill.md` instead of `SKILL.md` — fix the case on the source agent first. |
157
+ | `skip-unsupported` | MCP servers only — that agent's real definition can't be safely represented (see below); nothing was written for it. |
126
158
 
127
159
  Migrate never overwrites a genuine conflict, and never scopes a migrated
128
- skill to just the source agent — once in canonical, it's visible to every
129
- agent by default (see `sync`, below). If migrate reports a `conflict`, open
130
- the two files it names and decide by hand which content should actually be
131
- canonical, then re-run.
160
+ skill (or MCP server) to just the source agent — once in canonical, it's
161
+ visible to every agent by default (see `sync`, below). If migrate reports
162
+ a `conflict`, open the two files/entries it names and decide by hand
163
+ which content should actually be canonical, then re-run.
164
+
165
+ **MCP servers** (claude-code, kiro, codex — not pi, which has no static
166
+ MCP config to read at all) migrate the same way, into
167
+ `~/.trellis/mcp/servers.yaml`:
168
+
169
+ ```
170
+ $ trellis migrate --from claude-code --only mcp
171
+ migrate --from claude-code
172
+ [create] mcp server "gitlab" — will add to servers.yaml
173
+ ```
174
+
175
+ Two known fidelity limits, named rather than silently worked around:
176
+
177
+ - **Codex remote servers migrate when they only use `url` and
178
+ `bearer_token_env_var`** — the one shape this codebase has verified
179
+ against a real `codex` binary, and the only shape Trellis's own writer
180
+ ever produces for Codex. Codex's own config schema has no way to tell
181
+ `http` apart from `sse`, so a migrated remote server always comes back
182
+ as `http` — not a guess, that distinction was never stored in the
183
+ first place. A server using Codex's other header mechanisms
184
+ (`http_headers`/`env_http_headers`/`http_headers_helper` — real fields
185
+ this project has no verified shape for) is reported `skip-unsupported`
186
+ rather than guessed at; use `trellis mcp add` for that one server as a
187
+ workaround.
188
+ - **`headers` recovery depends on that agent's own real on-disk shape.**
189
+ claude-code/kiro read `headers` from the exact same JSON field Trellis
190
+ itself writes (`schema/servers.example.yaml`'s `figma` example) — if a
191
+ server was hand-authored with some other shape, it migrates whatever
192
+ is actually there, same as any other field.
132
193
 
133
194
  ## Starting from nothing
134
195
 
135
196
  Skip migrate. Edit `~/.trellis/agents.md` and add skills under
136
- `~/.trellis/skills/<name>/SKILL.md` directly. There's nothing else to set up
137
- before moving on to `sync`.
197
+ `~/.trellis/skills/<name>/SKILL.md` directly, or use
198
+ `trellis skill add` (below) instead of hand-editing. There's nothing else
199
+ to set up before moving on to `sync`.
200
+
201
+ The same applies to MCP servers — `~/.trellis/mcp/servers.yaml` can be
202
+ hand-authored the same way (see
203
+ [`schema/servers.example.yaml`](../schema/servers.example.yaml) for the
204
+ full shape), or use `trellis mcp add` (below). Either way, move on to
205
+ [`trellis mcp sync`](#trellis-mcp-sync) once you've added what you want.
206
+
207
+ ## `trellis skill` / `trellis mcp` — canonical CRUD via the CLI
208
+
209
+ An alternative to hand-editing canonical files directly — useful for
210
+ scripting, or when you'd rather not open a text editor for a one-line
211
+ change. Both commands are canonical-side only: they never touch any
212
+ agent's native config (that stays `sync`/`mcp sync`'s job).
213
+
214
+ ```
215
+ $ trellis skill list
216
+ my-skill — claude-code, codex, pi
217
+
218
+ $ trellis skill add my-other-skill --from ./some/local/dir
219
+ skill add my-other-skill
220
+ [create] will copy from ./some/local/dir
221
+
222
+ $ trellis skill remove my-other-skill
223
+ removed skill "my-other-skill" from canonical source.
224
+ ```
225
+
226
+ `skill add` refuses (no write) if the name already exists with different
227
+ content — same conflict posture as `migrate`, never silently overwritten.
228
+ `skill remove` deletes the canonical directory; the *next* `trellis sync`
229
+ then auto-removes the now-stale symlink on every agent that had it (skills
230
+ carry their own ownership marker — the symlink itself — so this
231
+ propagates automatically, unlike MCP servers below).
232
+
233
+ ```
234
+ $ trellis mcp list
235
+ tanka (http) — codex, pi
236
+ env: TANKA_TOKEN (values never read/printed)
237
+
238
+ $ trellis mcp add local-server --transport stdio --command node --args server.js --env API_KEY
239
+ mcp add local-server
240
+ [create] will add to servers.yaml
241
+
242
+ $ trellis mcp remove local-server
243
+ removed MCP server "local-server" from canonical source.
244
+ ```
245
+
246
+ `mcp add` takes `--transport stdio|http|sse`; stdio requires `--command`
247
+ (plus optional `--args a,b`, `--env NAME,...`, `--static-env k=v,...`),
248
+ http/sse require `--url` (plus optional `--headers k=v,...`, values
249
+ expected as `${VAR}` references, never literal secrets). Both accept
250
+ `--agents id,...` (scope) and `--enabled true|false`. Same no-overwrite
251
+ conflict posture as `skill add` — there is no `--force`.
252
+
253
+ `mcp remove` is **canonical-only** — it does not remove the server from
254
+ any agent that already has it from an earlier `mcp sync` (the same
255
+ no-automatic-removal gap `mcp sync` itself has — see
256
+ [README's Known limitations](../README.md#status)); remove it by hand on
257
+ each agent in the meantime.
258
+
259
+ `mcp list` never resolves or prints a secret value: `env` names are shown
260
+ as bare names (the actual value is never read from your shell), and
261
+ `static_env` values are shown in full since those were never secrets in
262
+ the first place (see `mcp sync`'s own explanation of `env` vs.
263
+ `static_env`, further below).
264
+
265
+ All four of `skill add`/`skill remove`/`mcp add`/`mcp remove` support
266
+ `--dry-run` and `--json`, same convention as every other command.
138
267
 
139
268
  ## `trellis sync`
140
269
 
141
- Distributes canonical skills and instructions to every agent present on this
142
- machine:
270
+ Distributes canonical skills and instructions to every **managed** agent
271
+ (`~/.trellis/managed.yaml` — empty by default; `trellis onboard` writes it,
272
+ or edit it yourself). A present-but-unmanaged agent gets no report line at
273
+ all, not just zero items:
143
274
 
144
275
  ```
145
276
  $ trellis sync
@@ -162,12 +293,18 @@ canonical, that's what `migrate` is for.
162
293
  Run `trellis sync skills` or `trellis sync instructions` to distribute just
163
294
  one half. Add `--dry-run` (in any position — `trellis sync --dry-run` and
164
295
  `trellis sync skills --dry-run` both work) to preview the plan with zero
165
- writes.
296
+ writes (and, per the same rule, records nothing to back up — see
297
+ `trellis rollback` below).
298
+
299
+ Every create/repair/remove this actually performs is recorded first,
300
+ automatically, so `trellis rollback` can undo the whole run later — see
301
+ [`trellis rollback`](#trellis-rollback--undoing-a-syncmcp-synconboard-run)
302
+ below.
166
303
 
167
304
  ## `trellis mcp sync`
168
305
 
169
- Distributes `~/.trellis/mcp/servers.yaml` to every present agent's native
170
- MCP config. See [`schema/servers.example.yaml`](../schema/servers.example.yaml)
306
+ Distributes `~/.trellis/mcp/servers.yaml` to every **managed** agent's
307
+ native MCP config — same restriction as `sync`, see above. See [`schema/servers.example.yaml`](../schema/servers.example.yaml)
171
308
  for the full documented shape — server definitions, per-agent scoping,
172
309
  known-host-injected collision avoidance, and hub mode.
173
310
 
@@ -180,6 +317,11 @@ does not remove it from any agent's native config yet (see
180
317
  [README's Known limitations](../README.md#status)). Remove it by hand on
181
318
  each agent in the meantime.
182
319
 
320
+ Every native-config file this rewrites in place is snapshotted first,
321
+ automatically — see
322
+ [`trellis rollback`](#trellis-rollback--undoing-a-syncmcp-synconboard-run)
323
+ below to undo a run that turned out to be wrong.
324
+
183
325
  `env:` in `servers.yaml` lists variable **names** only, never literal
184
326
  values — the real values come from wherever your shell/secret manager
185
327
  already populates them. See
@@ -187,13 +329,61 @@ already populates them. See
187
329
  for exactly how that resolution works for each agent, including the one
188
330
  narrow exception (pi's bridge has to read a value into its own process).
189
331
 
332
+ Before writing any name-only `env` entry, `mcp sync` checks it actually
333
+ resolves — a name with no value anywhere `secrets audit` would also
334
+ check is refused as a conflict for that one server, not written and left
335
+ to silently break that server once the agent tries to use it. Every
336
+ other server, and every other agent, still syncs normally.
337
+
338
+ For a value that isn't a secret at all — an email address, an
339
+ environment tag — use `static_env` instead of `env`: written into the
340
+ agent's config verbatim, never treated as a name to resolve (still
341
+ scanned for an accidental real credential, same as every other literal
342
+ field). `enabled: false` keeps a server's definition in canonical
343
+ without writing it to any agent — for something you want configured but
344
+ not currently active anywhere; remove the line (or set it `true`) to
345
+ turn it back on everywhere at once. See
346
+ [`schema/servers.example.yaml`](../schema/servers.example.yaml) for both.
347
+
348
+ ## `trellis memory sync`
349
+
350
+ Ingests `~/.trellis/memories/*.md` into the actual on-disk file
351
+ `@modelcontextprotocol/server-memory` reads at its own startup — closing
352
+ the gap between "memory entries exist in canonical" and "the running
353
+ memory server actually knows about them." Requires a `memory` server in
354
+ `servers.yaml` with `static_env.MEMORY_FILE_PATH` set explicitly (see
355
+ [`schema/servers.example.yaml`](../schema/servers.example.yaml)); without
356
+ one, this is a no-op, not an error.
357
+
358
+ ```
359
+ $ trellis memory sync
360
+ memory sync — /Users/you/.trellis/memories/graph.jsonl
361
+ [create] "sprint-tasks" — will create a new entity
362
+ ```
363
+
364
+ Every entry Trellis creates is tagged internally so a later sync can
365
+ safely update or remove it; anything else already in that file — an
366
+ entity or relation an agent added itself while actually using the memory
367
+ server — is never touched. If a name collides with something already in
368
+ the graph that Trellis didn't create, the command refuses that one entry
369
+ (reported as a conflict) rather than overwriting it, same posture as
370
+ every other conflict in this project.
371
+
372
+ **Known limitation, named rather than silently worked around:** this only
373
+ ingests canonical's *own* `memories/*.md` files into the shared store —
374
+ it does not (yet) extract an agent's own already-accumulated memory
375
+ content (e.g. Claude Code's own per-project memory feature) back into
376
+ canonical. That extraction is a real, separate, still-open gap — see
377
+ `docs/roadmap.md`'s P15 entry.
378
+
190
379
  ## `trellis secrets audit`
191
380
 
192
381
  ```
193
382
  $ trellis secrets audit
194
383
  ```
195
384
 
196
- Scans every present agent's **real, on-disk** config (never canonical) for:
385
+ Scans every **managed** agent's **real, on-disk** config (never canonical)
386
+ for — same restriction as `sync`, see above:
197
387
 
198
388
  1. A literal value matching one of `secrets.policy.yaml`'s
199
389
  `reject_patterns` (a credential-shaped string that should have been a
@@ -220,6 +410,50 @@ Safe to run any time; nothing here writes anything.
220
410
  stdio MCP server (some reaching real external services) — not something a
221
411
  "just check my config" command should do by default.
222
412
 
413
+ ## `trellis rollback` — undoing a `sync`/`mcp sync`/`onboard` run
414
+
415
+ Every real write those three commands perform is recorded, before it
416
+ happens, to a structured run directory under `~/.trellis/backups/` — no
417
+ flag needed, this is always on for any run that actually writes
418
+ something. `--dry-run` never creates one, since nothing was written.
419
+
420
+ ```
421
+ $ trellis rollback
422
+ ```
423
+
424
+ Omitting a run id targets the most recent run. Pass one explicitly to
425
+ undo an older run — see `trellis rollback --list` for what's available:
426
+
427
+ ```
428
+ $ trellis rollback --list
429
+ 2026-09-13T04-52-18-727Z-mcp-sync — mcp-sync, 3 operation(s), 2026-09-13T04:52:18.727Z
430
+ 2026-09-13T04-44-56-967Z-sync — sync, 1 operation(s), 2026-09-13T04:44:56.967Z
431
+ ```
432
+
433
+ For each recorded operation, rollback checks whether that path's
434
+ **current** state still matches what the run itself left behind:
435
+
436
+ | Current state vs. recorded | Result |
437
+ |---|---|
438
+ | Unchanged since the run | `restore` — the file's exact prior bytes, or the symlink's exact prior target, or removed if the run created it |
439
+ | Something else touched it since | `conflict` — reported, left untouched, never force-restored over |
440
+
441
+ One path's `conflict` never blocks any other path in the same rollback
442
+ from restoring. Exit code is non-zero if any `conflict` occurred. Add
443
+ `--dry-run` to preview the restore/conflict plan with zero writes, or
444
+ `--json` for machine-readable output.
445
+
446
+ `onboard` shares one backup run across its whole chained `sync`/`mcp
447
+ sync` stages — one `trellis rollback` undoes an entire `onboard`
448
+ invocation, not just its last stage. `migrate` is not covered: it only
449
+ ever creates a new canonical entry or refuses on conflict, never
450
+ overwrites existing canonical content, so there's nothing a snapshot
451
+ would add — undoing a migrate mistake is just deleting the newly
452
+ created file under `~/.trellis/skills/`.
453
+
454
+ `~/.trellis/backups/` has no automatic pruning — delete old run
455
+ directories by hand once you're done with them.
456
+
223
457
  ## Troubleshooting
224
458
 
225
459
  - **A `conflict` I don't understand**: `migrate` and `sync` both name the