agent-trellis 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/adapters/claude-code.d.ts +3 -2
- package/dist/adapters/claude-code.js +21 -8
- package/dist/adapters/codex.d.ts +6 -3
- package/dist/adapters/codex.js +41 -10
- package/dist/adapters/jsonMcp.d.ts +15 -5
- package/dist/adapters/jsonMcp.js +38 -29
- package/dist/adapters/kiro.d.ts +3 -2
- package/dist/adapters/kiro.js +22 -9
- package/dist/adapters/mcpPlan.d.ts +11 -6
- package/dist/adapters/mcpPlan.js +39 -6
- package/dist/cli.js +123 -10
- package/dist/commands/mcp.d.ts +101 -7
- package/dist/commands/mcp.js +227 -10
- package/dist/commands/memory.d.ts +47 -0
- package/dist/commands/memory.js +89 -0
- package/dist/commands/migrate.d.ts +30 -4
- package/dist/commands/migrate.js +83 -16
- package/dist/commands/onboard.d.ts +27 -8
- package/dist/commands/onboard.js +144 -17
- package/dist/commands/skill.d.ts +51 -0
- package/dist/commands/skill.js +104 -0
- package/dist/core/adapter.d.ts +18 -8
- package/dist/core/canonical.d.ts +26 -1
- package/dist/core/canonical.js +81 -3
- package/dist/core/types.d.ts +17 -0
- package/dist/lib/deepEqual.d.ts +8 -0
- package/dist/lib/deepEqual.js +26 -0
- package/dist/lib/dirEquals.d.ts +9 -0
- package/dist/lib/dirEquals.js +15 -1
- package/dist/lib/mcpMigrateRead.d.ts +69 -0
- package/dist/lib/mcpMigrateRead.js +188 -0
- package/dist/lib/mcpOwnership.d.ts +25 -0
- package/dist/lib/mcpOwnership.js +50 -0
- package/dist/lib/memoryGraph.d.ts +60 -0
- package/dist/lib/memoryGraph.js +101 -0
- package/dist/lib/realHomeSnapshot.d.ts +26 -0
- package/dist/lib/realHomeSnapshot.js +77 -0
- package/dist/lib/terminalPicker.d.ts +45 -0
- package/dist/lib/terminalPicker.js +193 -0
- package/dist/lib/tomlSection.d.ts +20 -6
- package/dist/lib/tomlSection.js +78 -12
- package/dist/pi-bridge/bundle.js +76 -46
- package/dist/pi-bridge/index.js +7 -2
- package/dist/probes/codex.js +10 -2
- package/docs/architecture.md +7 -4
- package/docs/getting-started.md +180 -15
- package/docs/roadmap.md +341 -0
- package/package.json +1 -1
- package/schema/servers.example.yaml +39 -2
package/dist/probes/codex.js
CHANGED
|
@@ -36,16 +36,24 @@ export async function probe(homeDir = homedir(), opts = {}) {
|
|
|
36
36
|
return { agent: "codex", present: false, skillRoots: [], mcpServers: [], diagnostics: [] };
|
|
37
37
|
}
|
|
38
38
|
const diagnostics = [];
|
|
39
|
+
// `codex` resolves its own config via $HOME (confirmed by running it with
|
|
40
|
+
// an overridden HOME against an empty scratch dir — it returns `[]`, not
|
|
41
|
+
// the real machine's servers), so every subprocess call here must be
|
|
42
|
+
// scoped to `homeDir` explicitly — otherwise a caller probing a non-
|
|
43
|
+
// default `homeDir` would silently get this real machine's real MCP
|
|
44
|
+
// server list instead (trellis-migrate-mcp-servers found this gap in
|
|
45
|
+
// src/lib/mcpMigrateRead.ts's own equivalent call; fixed there first).
|
|
46
|
+
const codexEnv = { ...process.env, HOME: homeDir };
|
|
39
47
|
let version;
|
|
40
48
|
try {
|
|
41
|
-
version = execFileSync("codex", ["--version"], { encoding: "utf-8", timeout: 5_000 }).trim();
|
|
49
|
+
version = execFileSync("codex", ["--version"], { encoding: "utf-8", timeout: 5_000, env: codexEnv }).trim();
|
|
42
50
|
}
|
|
43
51
|
catch {
|
|
44
52
|
// config exists without the binary on PATH — unusual, still probeable
|
|
45
53
|
}
|
|
46
54
|
let mcpEntries = [];
|
|
47
55
|
try {
|
|
48
|
-
const raw = execFileSync("codex", ["mcp", "list", "--json"], { encoding: "utf-8", timeout: 5_000 });
|
|
56
|
+
const raw = execFileSync("codex", ["mcp", "list", "--json"], { encoding: "utf-8", timeout: 5_000, env: codexEnv });
|
|
49
57
|
mcpEntries = JSON.parse(raw);
|
|
50
58
|
}
|
|
51
59
|
catch (err) {
|
package/docs/architecture.md
CHANGED
|
@@ -45,10 +45,13 @@ Aligned to the `.agents Protocol` draft, extended where the draft is silent:
|
|
|
45
45
|
├── memories/*.md # shared memory entries (server-memory backed)
|
|
46
46
|
├── scope.yaml # exceptions to "shared with all agents" —
|
|
47
47
|
│ # see "Private / agent-specific capabilities"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
└── secrets.policy.yaml # which var names are allowed, nothing else
|
|
49
|
+
|
|
50
|
+
# Not user-authored, so not shown in the tree above — Trellis's own
|
|
51
|
+
# bookkeeping, written/read only by `trellis mcp sync` itself:
|
|
52
|
+
# mcp/ownership.json # what Trellis last wrote per (agent, server name) —
|
|
53
|
+
# # the ownership marker MCP removal needed
|
|
54
|
+
# # (docs/roadmap.md P14, src/lib/mcpOwnership.ts)
|
|
52
55
|
```
|
|
53
56
|
|
|
54
57
|
## Global vs. workspace scope
|
package/docs/getting-started.md
CHANGED
|
@@ -17,18 +17,27 @@ $ trellis onboard
|
|
|
17
17
|
|
|
18
18
|
Runs `init`, detects which of Claude Code/Codex/Kiro/pi are on this machine,
|
|
19
19
|
then resolves two independent choices before running `migrate`, `sync`,
|
|
20
|
-
`mcp sync`, and `secrets audit` — the whole onboarding path,
|
|
21
|
-
commands to type by hand:
|
|
20
|
+
`mcp sync`, `memory sync`, and `secrets audit` — the whole onboarding path,
|
|
21
|
+
no follow-up commands to type by hand:
|
|
22
22
|
|
|
23
23
|
1. **Migration source** — read from, at most one, never written back to.
|
|
24
|
+
"Real content" means skills, custom instructions, *or* real MCP servers
|
|
25
|
+
— an agent whose only real content is its MCP servers is still a valid
|
|
26
|
+
source.
|
|
24
27
|
- **No agent has real content**: skipped — canonical starts from `init`'s
|
|
25
28
|
placeholder.
|
|
26
29
|
- **Exactly one agent has real content**: auto-selected, no prompt.
|
|
27
|
-
- **Two or more**: prompts you
|
|
28
|
-
terminal
|
|
30
|
+
- **Two or more**: prompts you to choose (Up/Down or j/k, Enter to confirm,
|
|
31
|
+
on a real terminal that supports it — falls back to a numbered
|
|
32
|
+
type-a-digit prompt otherwise), or pass `--agent <id>` to skip the
|
|
33
|
+
prompt entirely. Which *categories* to bring in from that source
|
|
34
|
+
(skills, instructions, mcp) is its own checkbox, shown only when the
|
|
35
|
+
source has real content in two or more of them — with just one, that
|
|
36
|
+
one is migrated unprompted.
|
|
29
37
|
2. **Managed set** — zero or more agents to actually write to. Always an
|
|
30
38
|
explicit choice: pass `--manage <ids>` (comma-separated, e.g. `--manage
|
|
31
|
-
pi,codex`) or `--manage none`, or answer the
|
|
39
|
+
pi,codex`) or `--manage none`, or answer the interactive checkbox prompt
|
|
40
|
+
(Space to toggle, Enter to confirm — same numbered fallback as above).
|
|
32
41
|
**The source is not included by default** — migrating from Claude Code
|
|
33
42
|
doesn't mean Trellis starts managing Claude Code too, unless you say so.
|
|
34
43
|
Selecting an agent that isn't installed yet is itself the authorization to
|
|
@@ -51,6 +60,9 @@ sync
|
|
|
51
60
|
mcp sync
|
|
52
61
|
✅ pi — already in sync
|
|
53
62
|
|
|
63
|
+
memory sync
|
|
64
|
+
no "memory" MCP server with static_env.MEMORY_FILE_PATH configured in servers.yaml — see schema/servers.example.yaml
|
|
65
|
+
|
|
54
66
|
secrets audit
|
|
55
67
|
✅ no findings — every present agent's real config and every declared env var passed all checks
|
|
56
68
|
```
|
|
@@ -60,9 +72,9 @@ it's present and was the migration source, but it isn't managed, so it's
|
|
|
60
72
|
never even probed as a sync target, not just left with zero items.
|
|
61
73
|
|
|
62
74
|
Add `--dry-run` to preview the entire chain — init/migrate/sync/mcp
|
|
63
|
-
sync, including what would be written to
|
|
64
|
-
zero writes anywhere (secrets audit is
|
|
65
|
-
the flag).
|
|
75
|
+
sync/memory sync, including what would be written to
|
|
76
|
+
`~/.trellis/managed.yaml` — with zero writes anywhere (secrets audit is
|
|
77
|
+
always read-only, with or without the flag).
|
|
66
78
|
|
|
67
79
|
**A managed agent's own real content still isn't overwritten.** If you
|
|
68
80
|
explicitly include the source in `--manage`, sync still never overwrites
|
|
@@ -134,27 +146,133 @@ Add `--dry-run` to see the plan without writing anything:
|
|
|
134
146
|
$ trellis migrate --from codex --dry-run
|
|
135
147
|
```
|
|
136
148
|
|
|
149
|
+
Add `--only skills`, `--only instructions`, or `--only mcp` to migrate
|
|
150
|
+
just one category — useful when you only want part of it brought in
|
|
151
|
+
right now. Omit it to migrate all three, exactly as above:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
$ trellis migrate --from codex --only instructions
|
|
155
|
+
```
|
|
156
|
+
|
|
137
157
|
**What each action means:**
|
|
138
158
|
|
|
139
159
|
| Action | Meaning |
|
|
140
160
|
|---|---|
|
|
141
161
|
| `create` | New to canonical source — copied in. |
|
|
142
|
-
| `already-migrated` | Canonical already has byte-identical content (safe re-run, nothing happens). |
|
|
162
|
+
| `already-migrated` | Canonical already has byte-identical (or, for MCP servers, structurally identical) content (safe re-run, nothing happens). |
|
|
143
163
|
| `conflict` | Canonical already has *different* real content — **left untouched**, resolve by hand. |
|
|
144
164
|
| `skip-symlink` | That agent's own copy is itself a symlink (already shared in from elsewhere) — nothing of that agent's own to import. |
|
|
145
165
|
| `skip-case-broken` | Found as `skill.md` instead of `SKILL.md` — fix the case on the source agent first. |
|
|
166
|
+
| `skip-unsupported` | MCP servers only — that agent's real definition can't be safely represented (see below); nothing was written for it. |
|
|
146
167
|
|
|
147
168
|
Migrate never overwrites a genuine conflict, and never scopes a migrated
|
|
148
|
-
skill to just the source agent — once in canonical, it's
|
|
149
|
-
agent by default (see `sync`, below). If migrate reports
|
|
150
|
-
the two files it names and decide by hand
|
|
151
|
-
canonical, then re-run.
|
|
169
|
+
skill (or MCP server) to just the source agent — once in canonical, it's
|
|
170
|
+
visible to every agent by default (see `sync`, below). If migrate reports
|
|
171
|
+
a `conflict`, open the two files/entries it names and decide by hand
|
|
172
|
+
which content should actually be canonical, then re-run.
|
|
173
|
+
|
|
174
|
+
**MCP servers** (claude-code, kiro, codex — not pi, which has no static
|
|
175
|
+
MCP config to read at all) migrate the same way, into
|
|
176
|
+
`~/.trellis/mcp/servers.yaml`:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
$ trellis migrate --from claude-code --only mcp
|
|
180
|
+
migrate --from claude-code
|
|
181
|
+
[create] mcp server "gitlab" — will add to servers.yaml
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Two known fidelity limits, named rather than silently worked around:
|
|
185
|
+
|
|
186
|
+
- **Codex remote servers migrate when they only use `url` and
|
|
187
|
+
`bearer_token_env_var`** — the one shape this codebase has verified
|
|
188
|
+
against a real `codex` binary, and the only shape Trellis's own writer
|
|
189
|
+
ever produces for Codex. Codex's own config schema has no way to tell
|
|
190
|
+
`http` apart from `sse`, so a migrated remote server always comes back
|
|
191
|
+
as `http` — not a guess, that distinction was never stored in the
|
|
192
|
+
first place. A server using Codex's other header mechanisms
|
|
193
|
+
(`http_headers`/`env_http_headers`/`http_headers_helper` — real fields
|
|
194
|
+
this project has no verified shape for) is reported `skip-unsupported`
|
|
195
|
+
rather than guessed at; use `trellis mcp add` for that one server as a
|
|
196
|
+
workaround.
|
|
197
|
+
- **`headers` recovery depends on that agent's own real on-disk shape.**
|
|
198
|
+
claude-code/kiro read `headers` from the exact same JSON field Trellis
|
|
199
|
+
itself writes (`schema/servers.example.yaml`'s `figma` example) — if a
|
|
200
|
+
server was hand-authored with some other shape, it migrates whatever
|
|
201
|
+
is actually there, same as any other field.
|
|
152
202
|
|
|
153
203
|
## Starting from nothing
|
|
154
204
|
|
|
155
205
|
Skip migrate. Edit `~/.trellis/agents.md` and add skills under
|
|
156
|
-
`~/.trellis/skills/<name>/SKILL.md` directly
|
|
157
|
-
|
|
206
|
+
`~/.trellis/skills/<name>/SKILL.md` directly, or use
|
|
207
|
+
`trellis skill add` (below) instead of hand-editing. There's nothing else
|
|
208
|
+
to set up before moving on to `sync`.
|
|
209
|
+
|
|
210
|
+
The same applies to MCP servers — `~/.trellis/mcp/servers.yaml` can be
|
|
211
|
+
hand-authored the same way (see
|
|
212
|
+
[`schema/servers.example.yaml`](../schema/servers.example.yaml) for the
|
|
213
|
+
full shape), or use `trellis mcp add` (below). Either way, move on to
|
|
214
|
+
[`trellis mcp sync`](#trellis-mcp-sync) once you've added what you want.
|
|
215
|
+
|
|
216
|
+
## `trellis skill` / `trellis mcp` — canonical CRUD via the CLI
|
|
217
|
+
|
|
218
|
+
An alternative to hand-editing canonical files directly — useful for
|
|
219
|
+
scripting, or when you'd rather not open a text editor for a one-line
|
|
220
|
+
change. Both commands are canonical-side only: they never touch any
|
|
221
|
+
agent's native config (that stays `sync`/`mcp sync`'s job).
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
$ trellis skill list
|
|
225
|
+
my-skill — claude-code, codex, pi
|
|
226
|
+
|
|
227
|
+
$ trellis skill add my-other-skill --from ./some/local/dir
|
|
228
|
+
skill add my-other-skill
|
|
229
|
+
[create] will copy from ./some/local/dir
|
|
230
|
+
|
|
231
|
+
$ trellis skill remove my-other-skill
|
|
232
|
+
removed skill "my-other-skill" from canonical source.
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`skill add` refuses (no write) if the name already exists with different
|
|
236
|
+
content — same conflict posture as `migrate`, never silently overwritten.
|
|
237
|
+
`skill remove` deletes the canonical directory; the *next* `trellis sync`
|
|
238
|
+
then auto-removes the now-stale symlink on every agent that had it (skills
|
|
239
|
+
carry their own ownership marker — the symlink itself — so this
|
|
240
|
+
propagates automatically, unlike MCP servers below).
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
$ trellis mcp list
|
|
244
|
+
tanka (http) — codex, pi
|
|
245
|
+
env: TANKA_TOKEN (values never read/printed)
|
|
246
|
+
|
|
247
|
+
$ trellis mcp add local-server --transport stdio --command node --args server.js --env API_KEY
|
|
248
|
+
mcp add local-server
|
|
249
|
+
[create] will add to servers.yaml
|
|
250
|
+
|
|
251
|
+
$ trellis mcp remove local-server
|
|
252
|
+
removed MCP server "local-server" from canonical source.
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`mcp add` takes `--transport stdio|http|sse`; stdio requires `--command`
|
|
256
|
+
(plus optional `--args a,b`, `--env NAME,...`, `--static-env k=v,...`),
|
|
257
|
+
http/sse require `--url` (plus optional `--headers k=v,...`, values
|
|
258
|
+
expected as `${VAR}` references, never literal secrets). Both accept
|
|
259
|
+
`--agents id,...` (scope) and `--enabled true|false`. Same no-overwrite
|
|
260
|
+
conflict posture as `skill add` — there is no `--force`.
|
|
261
|
+
|
|
262
|
+
`mcp remove` is **canonical-only** — it does not remove the server from
|
|
263
|
+
any agent that already has it from an earlier `mcp sync` (the same
|
|
264
|
+
no-automatic-removal gap `mcp sync` itself has — see
|
|
265
|
+
[README's Known limitations](../README.md#status)); remove it by hand on
|
|
266
|
+
each agent in the meantime.
|
|
267
|
+
|
|
268
|
+
`mcp list` never resolves or prints a secret value: `env` names are shown
|
|
269
|
+
as bare names (the actual value is never read from your shell), and
|
|
270
|
+
`static_env` values are shown in full since those were never secrets in
|
|
271
|
+
the first place (see `mcp sync`'s own explanation of `env` vs.
|
|
272
|
+
`static_env`, further below).
|
|
273
|
+
|
|
274
|
+
All four of `skill add`/`skill remove`/`mcp add`/`mcp remove` support
|
|
275
|
+
`--dry-run` and `--json`, same convention as every other command.
|
|
158
276
|
|
|
159
277
|
## `trellis sync`
|
|
160
278
|
|
|
@@ -220,6 +338,53 @@ already populates them. See
|
|
|
220
338
|
for exactly how that resolution works for each agent, including the one
|
|
221
339
|
narrow exception (pi's bridge has to read a value into its own process).
|
|
222
340
|
|
|
341
|
+
Before writing any name-only `env` entry, `mcp sync` checks it actually
|
|
342
|
+
resolves — a name with no value anywhere `secrets audit` would also
|
|
343
|
+
check is refused as a conflict for that one server, not written and left
|
|
344
|
+
to silently break that server once the agent tries to use it. Every
|
|
345
|
+
other server, and every other agent, still syncs normally.
|
|
346
|
+
|
|
347
|
+
For a value that isn't a secret at all — an email address, an
|
|
348
|
+
environment tag — use `static_env` instead of `env`: written into the
|
|
349
|
+
agent's config verbatim, never treated as a name to resolve (still
|
|
350
|
+
scanned for an accidental real credential, same as every other literal
|
|
351
|
+
field). `enabled: false` keeps a server's definition in canonical
|
|
352
|
+
without writing it to any agent — for something you want configured but
|
|
353
|
+
not currently active anywhere; remove the line (or set it `true`) to
|
|
354
|
+
turn it back on everywhere at once. See
|
|
355
|
+
[`schema/servers.example.yaml`](../schema/servers.example.yaml) for both.
|
|
356
|
+
|
|
357
|
+
## `trellis memory sync`
|
|
358
|
+
|
|
359
|
+
Ingests `~/.trellis/memories/*.md` into the actual on-disk file
|
|
360
|
+
`@modelcontextprotocol/server-memory` reads at its own startup — closing
|
|
361
|
+
the gap between "memory entries exist in canonical" and "the running
|
|
362
|
+
memory server actually knows about them." Requires a `memory` server in
|
|
363
|
+
`servers.yaml` with `static_env.MEMORY_FILE_PATH` set explicitly (see
|
|
364
|
+
[`schema/servers.example.yaml`](../schema/servers.example.yaml)); without
|
|
365
|
+
one, this is a no-op, not an error.
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
$ trellis memory sync
|
|
369
|
+
memory sync — /Users/you/.trellis/memories/graph.jsonl
|
|
370
|
+
[create] "sprint-tasks" — will create a new entity
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Every entry Trellis creates is tagged internally so a later sync can
|
|
374
|
+
safely update or remove it; anything else already in that file — an
|
|
375
|
+
entity or relation an agent added itself while actually using the memory
|
|
376
|
+
server — is never touched. If a name collides with something already in
|
|
377
|
+
the graph that Trellis didn't create, the command refuses that one entry
|
|
378
|
+
(reported as a conflict) rather than overwriting it, same posture as
|
|
379
|
+
every other conflict in this project.
|
|
380
|
+
|
|
381
|
+
**Known limitation, named rather than silently worked around:** this only
|
|
382
|
+
ingests canonical's *own* `memories/*.md` files into the shared store —
|
|
383
|
+
it does not (yet) extract an agent's own already-accumulated memory
|
|
384
|
+
content (e.g. Claude Code's own per-project memory feature) back into
|
|
385
|
+
canonical. That extraction is a real, separate, still-open gap — see
|
|
386
|
+
`docs/roadmap.md`'s P15 entry.
|
|
387
|
+
|
|
223
388
|
## `trellis secrets audit`
|
|
224
389
|
|
|
225
390
|
```
|
package/docs/roadmap.md
CHANGED
|
@@ -600,6 +600,334 @@ exactly that one path as a `conflict` (exit 1) while still correctly
|
|
|
600
600
|
restoring the other two, untouched, unaffected paths in the same
|
|
601
601
|
invocation.
|
|
602
602
|
|
|
603
|
+
**`trellis-mcp-static-env-and-disabled-servers`, implemented and
|
|
604
|
+
sandbox-verified** (modifies `mcp-server-sync`). Found by dogfooding
|
|
605
|
+
`mcp sync` against a real machine's actual, actively-used Codex
|
|
606
|
+
`config.toml` rather than a fixture: `[mcp_servers.supabase_db]` had
|
|
607
|
+
`enabled = false` (a definition kept on hand, deliberately off — no way
|
|
608
|
+
to represent that in canonical short of deleting it), and
|
|
609
|
+
`[mcp_servers.tanka]` used a hardcoded literal `env` table, not the
|
|
610
|
+
`env_vars` name-forwarding array every other real server on the same
|
|
611
|
+
machine uses. Migrating `tanka` as-is into the old names-only model
|
|
612
|
+
would have had `mcp sync` rewrite a working config into a broken one —
|
|
613
|
+
`TANKA_EMAIL`/`TANKA_ENV` were never real process env vars, just plain
|
|
614
|
+
values written straight into the file — and neither `mcp sync` nor
|
|
615
|
+
`secrets audit` would have caught it before the write happened.
|
|
616
|
+
|
|
617
|
+
`McpServerDef` gained `enabled?: boolean` (filtered in `resolveMcpPlan`
|
|
618
|
+
itself, the one choke point every adapter already funnels through — a
|
|
619
|
+
disabled server gets no write and no conflict, not just on Codex but on
|
|
620
|
+
every agent) and `staticEnv?: Record<string, string>` for a value that
|
|
621
|
+
was never a secret in the first place — Codex renders it as an adjacent
|
|
622
|
+
`[mcp_servers.<name>.env]` table (extending `tomlSection.ts`'s section
|
|
623
|
+
boundary logic to treat the pair as one atomic create/repair/remove
|
|
624
|
+
unit), Claude Code/Kiro merge it into the same `env` map their `${VAR}`
|
|
625
|
+
references already use. `resolveMcpPlan` also gained a mandatory fourth
|
|
626
|
+
`policy: SecretsPolicy` parameter: before writing any name-only `env`
|
|
627
|
+
entry, it now resolves it through the same `resolveSecretEnv` `secrets
|
|
628
|
+
audit`/the pi bridge already call, refusing (as a new conflict scoped to
|
|
629
|
+
just that server, on just that agent) a name that wouldn't actually
|
|
630
|
+
resolve — the exact silent-breakage scenario `tanka` would have hit.
|
|
631
|
+
|
|
632
|
+
The on-disk YAML key is `static_env` (snake_case, matching every other
|
|
633
|
+
multi-word key across `.trellis/*.yaml` — `known_host_injected`,
|
|
634
|
+
`allowed_vars` — translated to camelCase in `loadServersYaml`), a real
|
|
635
|
+
gap the design doc missed until implementation: `McpServerDef` had
|
|
636
|
+
always been parsed as-is with zero field translation, since every prior
|
|
637
|
+
field name happened to already be a single word.
|
|
638
|
+
|
|
639
|
+
Sandbox-verified, not just unit-tested: reproducing this machine's exact
|
|
640
|
+
`tanka`/`supabase-db` shapes in `test/fixtures/home` and running a real
|
|
641
|
+
`mcp sync` inside `scripts/sandbox.sh` produced Codex's two-table output
|
|
642
|
+
byte-for-byte identical to the real, working config this change was
|
|
643
|
+
motivated by — and surfaced one more real regression before it could
|
|
644
|
+
ship: the sandbox's own pre-existing fixture servers declare `env` names
|
|
645
|
+
that don't resolve inside the container, which the new pre-write check
|
|
646
|
+
would have refused outright. Fixed by exporting their fixture values in
|
|
647
|
+
`docker/entrypoint.sh`, matching what a real working setup would
|
|
648
|
+
actually have — found only because the fixture was actually run, not
|
|
649
|
+
just reasoned about.
|
|
650
|
+
|
|
651
|
+
Also removed a dead field from `schema/servers.example.yaml`: the
|
|
652
|
+
`figma` example's `auth: oauth` was never a real property on
|
|
653
|
+
`McpServerDef`, never read by any adapter, and never asserted by any
|
|
654
|
+
spec — the YAML parser has no field validation, so it silently did
|
|
655
|
+
nothing. No real OAuth support exists; the line taught a capability that
|
|
656
|
+
was never there.
|
|
657
|
+
|
|
658
|
+
**P11 is done and archived**
|
|
659
|
+
(`openspec/changes/archive/2026-09-13-trellis-migrate-category-selection/`;
|
|
660
|
+
modifies `canonical-source-migration` and `onboarding-flow`). `trellis
|
|
661
|
+
migrate --from <agent>` always planned skills and instructions
|
|
662
|
+
together, one unit, no subset selection — no CLI flag, and no
|
|
663
|
+
interactive picker for this choice either. `--only skills|instructions`
|
|
664
|
+
now restricts a run to just one category, filtered inside
|
|
665
|
+
`collectMigratePlan` itself (not computed then discarded) — the
|
|
666
|
+
excluded kind is never read for comparison and never appears in the
|
|
667
|
+
plan. `trellis onboard`'s migrate step gained a checkbox reusing
|
|
668
|
+
`src/lib/terminalPicker.ts` (the same module `trellis-onboard-
|
|
669
|
+
interactive-picker` built), but only offered when the resolved source
|
|
670
|
+
actually has both real skills and real instructions — a source with
|
|
671
|
+
only one real kind, or `--json`, or a terminal that can't support the
|
|
672
|
+
picker all default silently to migrating whichever kind(s) actually
|
|
673
|
+
have content, with no second, numbered-text fallback UI built, since
|
|
674
|
+
this choice never existed before this change to have a fallback for. An
|
|
675
|
+
empty selection is a valid, distinct outcome ("migrate skipped — no
|
|
676
|
+
categories selected"), not an error, and doesn't stop sync/mcp sync/
|
|
677
|
+
secrets audit from running. A real gap named rather than silently
|
|
678
|
+
folded in: `codex`/`pi`/`kiro` as migration sources remain untested —
|
|
679
|
+
`test/unit/migrate.test.ts` only ever exercises `claude-code`, despite
|
|
680
|
+
`collectMigratePlan`'s own dispatch being fully symmetric by design.
|
|
681
|
+
That's P13's job. Verified with 22 new unit tests (275/275 project-wide,
|
|
682
|
+
zero regressions) — no sandbox pass needed, this change touches no
|
|
683
|
+
adapter or native-config write path.
|
|
684
|
+
|
|
685
|
+
**P12 (✅ done, archived
|
|
686
|
+
[2026-09-13-trellis-canonical-cli-crud](../openspec/changes/archive/2026-09-13-trellis-canonical-cli-crud/)):
|
|
687
|
+
canonical CRUD via CLI.** Neither skills nor MCP servers had any
|
|
688
|
+
command-line add/remove/list surface — `cli.ts`'s `mcp` command
|
|
689
|
+
recognized exactly one subcommand, `sync`, and there was no `skill`
|
|
690
|
+
command at all. `trellis skill list/add/remove` and `trellis mcp
|
|
691
|
+
list/add/remove` are new, canonical-side-only commands (a new
|
|
692
|
+
`canonical-content-management` capability): `skill add`/`mcp add`
|
|
693
|
+
refuse (no write, no `--force`) on an existing name with different
|
|
694
|
+
content, mirroring `migrate`'s own conflict posture exactly, reusing
|
|
695
|
+
the same comparison logic via a newly-extracted
|
|
696
|
+
`decideDirImport(sourceDir, canonicalDir)` (src/lib/dirEquals.ts),
|
|
697
|
+
which also replaced `migrate.ts`'s own inline check
|
|
698
|
+
(behavior-preserving, confirmed by its full pre-existing suite passing
|
|
699
|
+
unmodified). `servers.yaml`'s new writer (`upsertServerYaml`/
|
|
700
|
+
`removeServerYaml` in `canonical.ts`) uses the `yaml` package's
|
|
701
|
+
`Document`-based `parseDocument`/`setIn`/`deleteIn`/`toString`, never a
|
|
702
|
+
full parse-then-restringify, specifically so a hand-authored file's
|
|
703
|
+
comments and untouched entries survive byte-for-byte — verified with a
|
|
704
|
+
dedicated test asserting exactly that. `skill remove` needed zero new
|
|
705
|
+
removal-propagation code: skills already carry an ownership marker (the
|
|
706
|
+
symlink itself), so `sync`'s pre-existing stale-symlink detection
|
|
707
|
+
un-syncs a removed skill automatically on the next run (proven
|
|
708
|
+
end-to-end in a test). `mcp remove` stays canonical-only by design —
|
|
709
|
+
MCP has no such marker yet, so an already-synced agent's native config
|
|
710
|
+
is untouched until P14 closes that gap. `mcp list` never resolves a
|
|
711
|
+
secret: `env` entries print as bare names (never read from
|
|
712
|
+
`process.env`), while `static_env` values print in full since they were
|
|
713
|
+
never secrets by `McpServerDef`'s own contract. Verified with 21 new
|
|
714
|
+
unit tests (296/296 project-wide, zero regressions) plus manual
|
|
715
|
+
smoke-testing of every subcommand (list/add/remove, conflict/
|
|
716
|
+
already-present/invalid-input, `--dry-run`, `--json`) against a
|
|
717
|
+
throwaway sandbox `$HOME` — no sandbox-container pass needed, this
|
|
718
|
+
change touches no adapter or native-config write path.
|
|
719
|
+
|
|
720
|
+
**P13 (✅ done, archived
|
|
721
|
+
[2026-09-13-trellis-real-sandbox-verification](../openspec/changes/archive/2026-09-13-trellis-real-sandbox-verification/)):
|
|
722
|
+
sandbox verification against this machine's real state.** Every sandbox
|
|
723
|
+
run before this (`scripts/sandbox.sh`, `docker/entrypoint.sh`) mounted
|
|
724
|
+
the same single, git-tracked synthetic fixture (`test/fixtures/home`).
|
|
725
|
+
Compounding this, `test/unit/migrate.test.ts` only ever exercised
|
|
726
|
+
`"claude-code"` as a migration source — codex/pi/kiro had never been
|
|
727
|
+
verified as sources even though `collectMigratePlan`'s `PROBES:
|
|
728
|
+
Record<AgentId, ...>` dispatch is fully symmetric by design (claude-code
|
|
729
|
+
was, however, already covered as a sync/mcp-sync *target*, in
|
|
730
|
+
`test/unit/sync.test.ts`/`test/unit/mcp.test.ts` — the earlier draft of
|
|
731
|
+
this entry claimed otherwise; corrected here after checking, not
|
|
732
|
+
assumed). `scripts/sandbox.sh --real` builds a throwaway snapshot from a
|
|
733
|
+
real `$HOME` using an **allowlist**, not the denylist first sketched here
|
|
734
|
+
— every path each probe (`src/probes/*.ts`) is already confirmed to
|
|
735
|
+
read, and nothing else, since this project has no complete knowledge of
|
|
736
|
+
where third-party agents' own real OAuth token flows store credentials
|
|
737
|
+
(design.md D1 explains the reasoning). Actually running this against a
|
|
738
|
+
real, in-use machine (not just reasoning about it) found two real bugs
|
|
739
|
+
before it could even complete: `sync`'s own real output is a symlink
|
|
740
|
+
back into `~/.trellis/`, which a naive symlink-preserving copy leaves
|
|
741
|
+
dangling once mounted into a container with no such path — fixed by
|
|
742
|
+
dereferencing during copy; and a case-insensitive filesystem (macOS
|
|
743
|
+
default) collides two of pi's own case-sensitive instructions-file
|
|
744
|
+
candidates (`AGENTS.md`/`AGENTS.MD`), which needed a
|
|
745
|
+
dest-already-exists guard to avoid a crash. Before any Docker build, the
|
|
746
|
+
snapshot is gated through `trellis secrets audit`'s own, already-shipped
|
|
747
|
+
`homeDir` seam — on the actual real-machine run, this correctly found 5
|
|
748
|
+
genuine `unexpected-var-name` findings and refused to proceed, exactly
|
|
749
|
+
as designed; clearing that machine's own `secrets.policy.yaml` gap and
|
|
750
|
+
running a full container pass against it remains a separate,
|
|
751
|
+
human-initiated action, not something this change forced through.
|
|
752
|
+
codex/kiro/pi as migrate sources are now covered in
|
|
753
|
+
`test/unit/migrateSources.test.ts`, using each probe's own confirmed
|
|
754
|
+
real dotfile paths. Verified with 10 new unit tests (306/306
|
|
755
|
+
project-wide) plus the fixture-based (default, non-`--real`)
|
|
756
|
+
`scripts/sandbox.sh` re-run end-to-end against Docker to confirm zero
|
|
757
|
+
regression to the existing path.
|
|
758
|
+
|
|
759
|
+
**P14 (✅ done — removal half only, archived
|
|
760
|
+
[2026-09-13-trellis-mcp-sync-removal](../openspec/changes/archive/2026-09-13-trellis-mcp-sync-removal/);
|
|
761
|
+
migrate-in remains open, see below): MCP server lifecycle parity with
|
|
762
|
+
skills.** Skills have a full migrate (import) + sync (create/repair) +
|
|
763
|
+
conflict story; MCP had sync only. This was always two distinct, real
|
|
764
|
+
gaps, not one — this change closed the harder, more clearly-specified
|
|
765
|
+
half: `resolveMcpPlan` was deliberately create/repair-only
|
|
766
|
+
(`mcpPlan.ts`'s own stated reasoning: a bare TOML/JSON key has no
|
|
767
|
+
ownership marker to prove Trellis, not the user, put it there), so
|
|
768
|
+
removing a server from `servers.yaml` never removed it from any
|
|
769
|
+
agent's native config. A new `src/lib/mcpOwnership.ts` ledger
|
|
770
|
+
(`~/.trellis/mcp/ownership.json`) records, per agent and server name,
|
|
771
|
+
the exact rendered value Trellis itself last wrote; on a later sync, a
|
|
772
|
+
name gone from canonical is only actually removed from an agent's
|
|
773
|
+
native config if that config's current entry still exactly matches
|
|
774
|
+
what the ledger recorded — a hand-edited entry is left alone,
|
|
775
|
+
indefinitely, never forced. Reused rather than rebuilt:
|
|
776
|
+
`src/lib/tomlSection.ts`'s `removeSection` (built earlier for the
|
|
777
|
+
static-env atomic-range work, never wired into an actual removal path
|
|
778
|
+
until now) for Codex, and each JSON agent's own existing `deepEqual`
|
|
779
|
+
for the "unchanged since" check. **Still genuinely open, not done
|
|
780
|
+
here:** MCP *migrate-in* (importing an already-hand-configured server
|
|
781
|
+
into canonical) — each static-config probe (`codex.ts`/`claude-code.ts`/
|
|
782
|
+
`kiro.ts`) still discards a server's complete real definition (e.g.
|
|
783
|
+
`codex mcp list --json`'s `CodexMcpEntry`, with `command`/`args`/
|
|
784
|
+
`env_vars` intact) down to `{name, transport, probe}`
|
|
785
|
+
(`AgentSnapshotMcpServer`) before anything downstream sees it, and
|
|
786
|
+
there is still no conversion from that discarded, richer shape into
|
|
787
|
+
canonical's `McpServerDef`. pi has no static config to read at all —
|
|
788
|
+
inherently import-less; P12's CLI remains its only route in. A real
|
|
789
|
+
gap, named rather than silently dropped, left for a future change.
|
|
790
|
+
Verified with 3 new/rewritten unit tests across both Claude Code's JSON
|
|
791
|
+
path and Codex's TOML path (308/308 project-wide, zero regressions).
|
|
792
|
+
|
|
793
|
+
**P15 (✅ done — ingestion half only, archived
|
|
794
|
+
[2026-09-13-trellis-memory-sync](../openspec/changes/archive/2026-09-13-trellis-memory-sync/);
|
|
795
|
+
per-agent extraction remains open, see below): shared memory — real
|
|
796
|
+
ingestion and per-agent extraction.** `memories` was parsed
|
|
797
|
+
(`canonical.ts`) into `CanonicalSource.memories: MemoryEntry[]` and
|
|
798
|
+
consumed nowhere — no adapter, no command, no `trellis memory` CLI
|
|
799
|
+
surface existed at all. P6 explicitly left "auto-ingesting
|
|
800
|
+
`~/.trellis/memories/*.md` content into the running memory server's
|
|
801
|
+
store" out of scope; `trellis memory sync` closes that gap (A): each
|
|
802
|
+
canonical memory file becomes one entity
|
|
803
|
+
(`entityType: "trellis-memory"`) in `@modelcontextprotocol/
|
|
804
|
+
server-memory`'s own on-disk JSON-lines graph file (the exact file that
|
|
805
|
+
server itself reads at startup — Trellis never spawns or talks to a
|
|
806
|
+
running server process, a plain file write like everything else this
|
|
807
|
+
project does), requiring `mcp/servers.yaml`'s `memory` server to set
|
|
808
|
+
`static_env.MEMORY_FILE_PATH` explicitly (the server's own unset-env
|
|
809
|
+
default resolves relative to wherever `npx` cached the package, not a
|
|
810
|
+
predictable location). The in-band `entityType` tag is the ownership
|
|
811
|
+
marker — deliberately not a separate ledger file like P14's MCP
|
|
812
|
+
removal, since (unlike per-agent MCP config) every agent connected to
|
|
813
|
+
this one server shares the exact same graph, so there's no per-agent
|
|
814
|
+
render to track. Every entity/relation Trellis didn't create is left
|
|
815
|
+
completely untouched, unconditionally; a name collision with a
|
|
816
|
+
non-Trellis-tagged entity is a conflict, never overwritten. **Still
|
|
817
|
+
genuinely open, not done here (B):** extracting an agent's own
|
|
818
|
+
already-accumulated memory content back into canonical — e.g. Claude
|
|
819
|
+
Code's own per-project memory feature. Investigated, not attempted, for
|
|
820
|
+
two concrete reasons: that content lives under
|
|
821
|
+
`~/.claude/projects/<project-slug>/memory/`, a path
|
|
822
|
+
`trellis-real-sandbox-verification`'s own allowlist already deliberately
|
|
823
|
+
excludes (mixed with real session transcripts); and the project-slug
|
|
824
|
+
encoding scheme Claude Code uses to derive that path from a working
|
|
825
|
+
directory has no authoritative documented source this project could
|
|
826
|
+
verify against, so it was not guessed at (this project's own "verify,
|
|
827
|
+
don't assume" discipline). Kiro's `totalrecallai` (SQLite + local vector
|
|
828
|
+
embeddings) is a further, genuinely different data shape, and was never
|
|
829
|
+
in scope for this half either. A real gap, named rather than silently
|
|
830
|
+
dropped, left for a future change. Verified with 14 new unit tests
|
|
831
|
+
(322/322 project-wide, zero regressions).
|
|
832
|
+
|
|
833
|
+
**P16 (✅ done, archived
|
|
834
|
+
[2026-09-13-trellis-migrate-mcp-servers](../openspec/changes/archive/2026-09-13-trellis-migrate-mcp-servers/)):
|
|
835
|
+
MCP migrate-in — the gap P14 named.** `trellis migrate --from <agent>`
|
|
836
|
+
gains a third `--only` value, `mcp`: claude-code, kiro, and codex each
|
|
837
|
+
get a new, purpose-built reader (`src/lib/mcpMigrateRead.ts`) that
|
|
838
|
+
converts that agent's real, already-configured MCP servers into
|
|
839
|
+
canonical's `McpServerDef` shape — kept entirely separate from each
|
|
840
|
+
probe's own thin `AgentSnapshotMcpServer` (`doctor`'s read path, left
|
|
841
|
+
untouched). Same conflict posture as skill/instructions migration
|
|
842
|
+
throughout: identical is a no-op, a differing definition under the same
|
|
843
|
+
name is a conflict, never overwritten. pi is still never a migrate-in
|
|
844
|
+
source — no static config to read, unchanged from P14's own framing.
|
|
845
|
+
Two fidelity limits, found by actually running each agent's real
|
|
846
|
+
tooling rather than assumed, are handled by refusing rather than
|
|
847
|
+
guessing: **Codex is stdio-transport only at first** (closed as a
|
|
848
|
+
same-day follow-up, see below) — `codex mcp list --json` had no
|
|
849
|
+
evidence in this codebase for any other transport shape at design time;
|
|
850
|
+
a real, one-off run against a locally-installed `codex-cli 0.154.0`
|
|
851
|
+
during implementation *did* observe a `streamable_http` shape (`url`,
|
|
852
|
+
`bearer_token_env_var`, plus three further undocumented fields) for a
|
|
853
|
+
hand-written `url`-based server — recorded as a concrete lead for a
|
|
854
|
+
future change, not built at first, since one data point from one
|
|
855
|
+
version isn't a contract; a non-stdio Codex server was reported
|
|
856
|
+
`skip-unsupported`, named, not silently dropped. **Codex's `static_env`**
|
|
857
|
+
is recovered by reading `[mcp_servers.<name>.env]` directly from
|
|
858
|
+
`config.toml` (a new `readServerEnvTable` in `tomlSection.ts`, the exact
|
|
859
|
+
inverse of that module's own existing writer) since `codex mcp list
|
|
860
|
+
--json` only ever reports variable *names*, never the literal table
|
|
861
|
+
Trellis itself renders separately. **`headers`** — missing from both
|
|
862
|
+
`claude-code.ts`'s and `kiro.ts`'s own probe-facing JSON types despite
|
|
863
|
+
being a real field Trellis's own writer (`jsonMcp.ts`) already produces
|
|
864
|
+
for http/sse servers — is recovered via a parallel, richer local type
|
|
865
|
+
scoped only to this new reader module, leaving both probes' existing,
|
|
866
|
+
`doctor`-tested types untouched. Verified with 19 new unit tests,
|
|
867
|
+
including one exercising the real, locally-installed `codex` binary
|
|
868
|
+
end-to-end against a scratch, HOME-scoped `.codex/config.toml` (341/341
|
|
869
|
+
project-wide, zero regressions). One related, pre-existing gap
|
|
870
|
+
surfaced but deliberately not fixed at first (out of scope for this
|
|
871
|
+
change, named instead): `src/probes/codex.ts`'s own `probe()` never
|
|
872
|
+
scoped its `codex mcp list --json` subprocess call to a passed-in
|
|
873
|
+
`homeDir` via `HOME` env override the way this change's own new reader
|
|
874
|
+
did — meaning that probe's MCP listing always reflected the real
|
|
875
|
+
machine's real codex config regardless of what `homeDir` a caller passed
|
|
876
|
+
it, an inconsistency with the rest of `probe()`'s own home-scoped reads.
|
|
877
|
+
|
|
878
|
+
**Same-day follow-up, both of the above closed (not a separately
|
|
879
|
+
numbered phase — direct fixes to already-shipped, already-specced
|
|
880
|
+
behavior, not new requirements, so no new OpenSpec change):**
|
|
881
|
+
`src/probes/codex.ts`'s `probe()` now scopes both of its `execFileSync`
|
|
882
|
+
calls to the passed-in `homeDir` the same way — verified with a new
|
|
883
|
+
regression test (`test/unit/codexProbe.test.ts`) using two distinct
|
|
884
|
+
scratch homes with two distinct MCP servers, confirming neither leaks
|
|
885
|
+
into the other; confirmed the test actually catches the bug by reverting
|
|
886
|
+
the fix and watching it fail against this real machine's real MCP
|
|
887
|
+
servers before restoring it. Separately, `buildCodexMcpReadResult`
|
|
888
|
+
(`src/lib/mcpMigrateRead.ts`) now converts a non-stdio Codex server when
|
|
889
|
+
it uses only `url` and, optionally, `bearer_token_env_var` — verified
|
|
890
|
+
this exact shape against the real `codex` binary a second time
|
|
891
|
+
(including with a real `bearer_token_env_var` set, not just a bare
|
|
892
|
+
`url`) — while a server using any of the three still-unexplained header
|
|
893
|
+
fields (`http_headers`/`env_http_headers`/`http_headers_helper`, all
|
|
894
|
+
`null` when unused, confirmed by the same real run) still refuses rather
|
|
895
|
+
than guesses. A migrated remote Codex server is always labeled `http`,
|
|
896
|
+
never `sse` — not a guess: Codex's own `config.toml` schema has no field
|
|
897
|
+
distinguishing the two (`upsertSection`'s own codex TOML rendering is
|
|
898
|
+
byte-identical for both transports, now covered by its own test), so
|
|
899
|
+
that distinction was never stored in the first place. 350/350 tests
|
|
900
|
+
passing.
|
|
901
|
+
|
|
902
|
+
**P17 (✅ done, archived
|
|
903
|
+
[2026-09-13-trellis-onboard-mcp-memory](../openspec/changes/archive/2026-09-13-trellis-onboard-mcp-memory/)):
|
|
904
|
+
`onboard` catches up to P15/P16 — mcp and memory were never wired into
|
|
905
|
+
its own orchestration.** Found by live dogfooding on a real machine:
|
|
906
|
+
`onboard`'s migrate-category picker hardcoded `["skills",
|
|
907
|
+
"instructions"]` — `mcp` was invisible to it regardless of how many real
|
|
908
|
+
MCP servers the source agent had — and `trellis memory sync` was never
|
|
909
|
+
called anywhere in `onboard`'s chain at all, despite its own docstring
|
|
910
|
+
naming a complete chain. `OnboardAgentSummary` gains an
|
|
911
|
+
`mcpServerCount`, computed via the already-exported
|
|
912
|
+
`collectMigratePlan(agent, homeDir, ["mcp"])` rather than a second reader
|
|
913
|
+
dispatch — so it always agrees with what `migrate` itself would do, and
|
|
914
|
+
an agent whose only real content is MCP servers is now a valid migration
|
|
915
|
+
source (previously invisible to `hasContent` entirely).
|
|
916
|
+
`resolveMigrateCategories` was rewritten from a fixed two-slot structure
|
|
917
|
+
to a dynamically-built list of whichever of skill/instructions/mcp have
|
|
918
|
+
real content, offering the checkbox only when two or more do — not a
|
|
919
|
+
bolted-on special case for a third option. `memory sync` runs as a new
|
|
920
|
+
final chained stage, after `mcp sync` and before `secrets audit`,
|
|
921
|
+
independent of the resolved managed-agent set (the memory server is
|
|
922
|
+
shared, not per-agent); `runMemorySync`'s inline printing was extracted
|
|
923
|
+
into an exported `printMemorySyncResult` so `onboard` reuses the exact
|
|
924
|
+
same output format rather than a second, drifting copy of it —
|
|
925
|
+
`memory.ts`'s own standalone command behavior is unchanged, confirmed by
|
|
926
|
+
its existing tests passing unmodified. No change to `migrate.ts`,
|
|
927
|
+
`mcp.ts`, or `memory.ts`'s own command behavior — purely `onboard`'s
|
|
928
|
+
orchestration layer catching up to commands that already existed. 357/357
|
|
929
|
+
tests passing.
|
|
930
|
+
|
|
603
931
|
| Phase | Deliverable | Depends on |
|
|
604
932
|
|---|---|---|
|
|
605
933
|
| P0 | ✅ `trellis doctor` — read-only, opt-in-for-handshakes scan of all four agents' current skills/MCP/instructions state, reports drift and duplicates | nothing |
|
|
@@ -613,5 +941,18 @@ invocation.
|
|
|
613
941
|
| P8 | ✅ Kiro `${VAR}` fix: adapter also manages `kiroAgent.mcpApprovedEnvVars`, without which Kiro silently never substitutes any env reference Trellis writes | P2 |
|
|
614
942
|
| P9 | ✅ MCP transport/auth expansion: `headers` field for static bearer/API-key remote auth (Claude Code/Codex/Kiro/pi bridge, each via its own real schema), `sse` transport; real OAuth flows explicitly delegated to each agent's own native support, not reimplemented | P2, P4, P8 |
|
|
615
943
|
| P10 | GUI: evaluate embedding into mcp-router's or skills-hub's existing interface before building anything new | P3–P9 |
|
|
944
|
+
| P11 | ✅ Migrate category selection: `--only skills\|instructions` on `migrate`, same interactive picker `onboard` already uses | P1 |
|
|
945
|
+
| P12 | ✅ Canonical CRUD via CLI: `trellis skill add/remove/list`, `trellis mcp add/remove/list` | P1, P2 |
|
|
946
|
+
| P13 | ✅ Sandbox verification against this machine's real, structurally-relevant state (not just fixtures); every agent verified as both migrate source and sync/mcp-sync target | P0–P2 |
|
|
947
|
+
| P14 | ✅ MCP server lifecycle parity with skills: ownership-tracked safe removal on sync (migrate-in closed separately by P16) | P2, P13 |
|
|
948
|
+
| P15 | ✅ Shared memory: ingest canonical `memories/*.md` into the `server-memory` store's own graph file (per-agent extraction into canonical remains a separate, open gap) | P6 |
|
|
949
|
+
| P16 | ✅ MCP migrate-in: `trellis migrate --only mcp` for claude-code/kiro/codex (codex remote transport + probe HOME-scoping closed same-day, see prose above), closing the gap P14 named | P12, P14 |
|
|
950
|
+
| P17 | ✅ `onboard` orchestration catches up to P15/P16: three-way migrate-category selection (mcp added), `memory sync` wired in as a chained stage | P15, P16 |
|
|
616
951
|
|
|
617
952
|
No dates. This is scoped by verification milestones, not calendar time.
|
|
953
|
+
Execution order for P11–P16: P11 → P12 → P13 → P14 → P15 → P16 — CRUD
|
|
954
|
+
(P12) lands before the larger features (P14, P15, P16) so each can be
|
|
955
|
+
adjusted via command line instead of hand-edited files while it's being
|
|
956
|
+
built, and the realistic sandbox (P13) lands before all three so each
|
|
957
|
+
gets verified against real data once, immediately, instead of against
|
|
958
|
+
another synthetic fixture.
|