agent-trellis 0.14.1 → 0.15.1
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/dist/adapters/claude-code.js +4 -3
- package/dist/adapters/codex.js +4 -3
- package/dist/adapters/kimi-code.js +4 -3
- package/dist/adapters/kiro.js +5 -4
- package/dist/adapters/pi.js +4 -3
- package/dist/adapters/zcode.d.ts +12 -1
- package/dist/adapters/zcode.js +7 -6
- package/dist/cli.js +52 -37
- package/dist/commands/doctor.d.ts +9 -1
- package/dist/commands/doctor.js +43 -6
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +24 -0
- package/dist/commands/mcp.d.ts +69 -3
- package/dist/commands/mcp.js +255 -56
- package/dist/commands/mcpGateway.js +3 -3
- package/dist/commands/migrate.d.ts +12 -0
- package/dist/commands/migrate.js +8 -4
- package/dist/commands/migrateWorkspace.d.ts +40 -0
- package/dist/commands/migrateWorkspace.js +197 -0
- package/dist/commands/rollback.js +65 -35
- package/dist/commands/skill.d.ts +14 -0
- package/dist/commands/skill.js +71 -0
- package/dist/commands/sync.d.ts +16 -1
- package/dist/commands/sync.js +48 -15
- package/dist/core/adapter.d.ts +14 -0
- package/dist/core/canonical.d.ts +20 -34
- package/dist/core/canonical.js +18 -92
- package/dist/core/mcpServerDef.d.ts +39 -0
- package/dist/core/mcpServerDef.js +93 -0
- package/dist/core/types.d.ts +39 -4
- package/dist/lib/cliArgs.d.ts +17 -0
- package/dist/lib/cliArgs.js +36 -0
- package/dist/lib/mcpMigrateRead.d.ts +25 -0
- package/dist/lib/mcpMigrateRead.js +9 -2
- package/dist/lib/scopeSelection.d.ts +1 -1
- package/dist/lib/scopeSelection.js +4 -3
- package/dist/lib/skillProvider.js +18 -5
- package/dist/lib/workspace.d.ts +39 -0
- package/dist/lib/workspace.js +158 -0
- package/dist/lib/workspaceConfig.d.ts +51 -0
- package/dist/lib/workspaceConfig.js +117 -0
- package/dist/lib/workspaceDoctor.d.ts +44 -0
- package/dist/lib/workspaceDoctor.js +94 -0
- package/dist/lib/workspaceFiles.d.ts +38 -0
- package/dist/lib/workspaceFiles.js +264 -0
- package/dist/lib/workspaceMcp.d.ts +57 -0
- package/dist/lib/workspaceMcp.js +277 -0
- package/dist/lib/workspaceMerge.d.ts +37 -0
- package/dist/lib/workspaceMerge.js +98 -0
- package/dist/lib/workspaceMigrate.d.ts +118 -0
- package/dist/lib/workspaceMigrate.js +423 -0
- package/dist/pi-bridge/bundle.js +258 -96
- package/docs/architecture.md +111 -49
- package/docs/getting-started.md +86 -14
- package/docs/mcp-oauth-matrix.md +12 -1
- package/docs/overview.zh-CN.md +17 -0
- package/docs/research.md +28 -6
- package/docs/roadmap.md +17 -5
- package/package.json +1 -1
- package/docs/implementation-plan.md +0 -341
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
# Implementation plan
|
|
2
|
-
|
|
3
|
-
Breaks `docs/roadmap.md`'s phases into concrete tasks. P0–P2 are specified at
|
|
4
|
-
file/function level since they're next. P3–P4 are specified at
|
|
5
|
-
module/decision level. P5–P7 stay at the roadmap's level of detail — planning
|
|
6
|
-
them further now would be speculative, since their shape depends on what P0–P4
|
|
7
|
-
actually reveal.
|
|
8
|
-
|
|
9
|
-
Every task below ends in something that can fail. If a task can't fail, it
|
|
10
|
-
isn't specified enough yet.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## P0 — `trellis doctor` (read-only scan, no canonical source required)
|
|
15
|
-
|
|
16
|
-
> **Superseded by `openspec/changes/trellis-doctor-p0/`.** The section below
|
|
17
|
-
> is kept as a historical note only — the authoritative proposal, design,
|
|
18
|
-
> specs, and tasks for P0 live there now. Don't edit this section further.
|
|
19
|
-
|
|
20
|
-
**Key simplification**: P0 does not require `.trellis/` to exist. It
|
|
21
|
-
cross-compares the four agents' *own current state* against each other and
|
|
22
|
-
flags drift/duplication directly — this is the exact manual process used
|
|
23
|
-
throughout `docs/research.md`'s investigation (symlink-vs-realpath checks,
|
|
24
|
-
MCP handshake probes, hash comparisons), now formalized into code instead of
|
|
25
|
-
one-off shell commands.
|
|
26
|
-
|
|
27
|
-
### 0.1 Shared probing library — `src/lib/`
|
|
28
|
-
|
|
29
|
-
- `src/lib/mcpProbe.ts`
|
|
30
|
-
- `probeMcpServer(def: McpServerDef, env: NodeJS.ProcessEnv, timeoutMs = 10_000): Promise<McpProbeResult>`
|
|
31
|
-
- Spawns the server, sends a JSON-RPC `initialize`, resolves on the first
|
|
32
|
-
`id:1` response or process exit, whichever comes first. This is a direct
|
|
33
|
-
port of the probe script used repeatedly by hand in this project's
|
|
34
|
-
research — it needs to become a tested module, not stay a throwaway
|
|
35
|
-
`/tmp/mcp-probe.mjs`.
|
|
36
|
-
- Fails the task if: a known-good server (e.g. `@modelcontextprotocol/server-memory`)
|
|
37
|
-
does not return `serverInfo` within the timeout in CI.
|
|
38
|
-
|
|
39
|
-
- `src/lib/fsIdentity.ts`
|
|
40
|
-
- `isSymlinkTo(path: string, target: string): boolean`
|
|
41
|
-
- `realpathDedupe(paths: string[]): Map<string /* realpath */, string[] /* original paths pointing there */>`
|
|
42
|
-
- This directly encodes the Codex finding from `docs/research.md`: dedup
|
|
43
|
-
must happen on realpath, not on path string or content hash alone
|
|
44
|
-
(two physical copies with identical content are *not* the same thing to
|
|
45
|
-
an agent's discovery logic — they show up as two entries).
|
|
46
|
-
- Fails the task if: given two physical copies of the same skill directory
|
|
47
|
-
(not symlinks), it correctly reports them as **not** deduplicated —
|
|
48
|
-
inverse of what `isSymlinkTo` would report for an actual symlink pair.
|
|
49
|
-
|
|
50
|
-
- `src/lib/skillFile.ts`
|
|
51
|
-
- `findSkillFile(dir: string): { path: string; caseCorrect: boolean } | null`
|
|
52
|
-
- Case-sensitivity check is explicit and reported, not silently corrected —
|
|
53
|
-
this is the exact bug (`skill.md` vs `SKILL.md`) found and fixed by hand
|
|
54
|
-
in this project's own history.
|
|
55
|
-
|
|
56
|
-
### 0.2 Per-agent probes — `src/probes/`
|
|
57
|
-
|
|
58
|
-
Each file implements `probe(): Promise<AgentSnapshot>` where
|
|
59
|
-
`AgentSnapshot` is a new type (add to `src/core/types.ts`):
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
interface AgentSnapshot {
|
|
63
|
-
agent: "claude-code" | "codex" | "kiro" | "pi";
|
|
64
|
-
present: boolean;
|
|
65
|
-
version?: string;
|
|
66
|
-
skillRoots: { path: string; isSymlink: boolean; target?: string; skillCount: number }[];
|
|
67
|
-
mcpServers: { name: string; source: "static-config" | "runtime-injected"; probe?: McpProbeResult }[];
|
|
68
|
-
instructionsFile?: { path: string; isSymlink: boolean; target?: string };
|
|
69
|
-
subagentsDir?: { path: string; isSymlink: boolean; count: number };
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
- `src/probes/claude-code.ts` — reads `~/.claude.json` (`mcpServers` key),
|
|
74
|
-
`~/.claude/skills`, `~/.claude/agents`, `~/.claude/CLAUDE.md`. Best-understood
|
|
75
|
-
agent; build this one first to validate the `AgentSnapshot` shape before
|
|
76
|
-
committing to it for the other three.
|
|
77
|
-
|
|
78
|
-
- `src/probes/codex.ts` — shells out to `codex mcp list` rather than
|
|
79
|
-
hand-parsing `config.toml` (TOML section patching in P2 will need real
|
|
80
|
-
parsing, but P0 is read-only and `codex` already exposes this). Skill root
|
|
81
|
-
is `~/.agents/skills` per Codex's own convention (confirmed in
|
|
82
|
-
`docs/research.md`) — the probe should also check whether `~/.codex/skills`
|
|
83
|
-
independently contains any *non-symlink* skill directories, since that's
|
|
84
|
-
exactly the duplication bug already found and fixed once in this
|
|
85
|
-
environment and is exactly the kind of regression `doctor` exists to catch.
|
|
86
|
-
|
|
87
|
-
- `src/probes/kiro.ts` — same shape as Claude Code:
|
|
88
|
-
`~/.kiro/settings/mcp.json`, `~/.kiro/skills`, `~/.kiro/steering/CLAUDE.md`.
|
|
89
|
-
|
|
90
|
-
- `src/probes/pi.ts` — **has an open unknown, resolve it first**: pi's skill
|
|
91
|
-
discovery path was never pinned down by direct observation in this
|
|
92
|
-
project's research (only confirmed pi *reads* `SKILL.md`-format files and
|
|
93
|
-
`AGENTS.md`/`CLAUDE.md`; the actual discovery directory wasn't isolated
|
|
94
|
-
from the minified bundle). Before writing this probe:
|
|
95
|
-
1. Run `pi --skill <path>` against a known skill dir and confirm it loads
|
|
96
|
-
(`pi -p "what skills do you have"` or similar, non-interactively).
|
|
97
|
-
2. Check `pi config` (interactive TUI) for any skill-path setting, and
|
|
98
|
-
check `~/.pi/agent/settings.json` again after that interaction — the
|
|
99
|
-
settings file only had four keys when checked during research; a skill
|
|
100
|
-
path may be set here that wasn't visible before because nothing had
|
|
101
|
-
configured one yet.
|
|
102
|
-
3. If no fixed discovery directory exists (i.e. skills are strictly
|
|
103
|
-
`--skill`-flag-only, per-invocation), document that as a real
|
|
104
|
-
constraint — the pi adapter in P4 would then need to inject the flag
|
|
105
|
-
via a wrapper rather than write to a filesystem location, which changes
|
|
106
|
-
P4's design meaningfully. **This determination gates P4's design, not
|
|
107
|
-
just P0's probe — do it early.**
|
|
108
|
-
|
|
109
|
-
### 0.3 Command — `src/commands/doctor.ts`
|
|
110
|
-
|
|
111
|
-
- `runDoctor(opts: { json?: boolean }): Promise<{ exitCode: number }>`
|
|
112
|
-
- Calls all four probes in parallel (`Promise.allSettled` — one agent being
|
|
113
|
-
absent or erroring must not blank out the other three's results).
|
|
114
|
-
- Cross-compares snapshots pairwise for:
|
|
115
|
-
- Same skill name present with different realpaths across agents (drift)
|
|
116
|
-
- Any skill root containing a non-symlinked physical copy where a symlink
|
|
117
|
-
pattern is otherwise established for that agent (duplication risk)
|
|
118
|
-
- MCP server names present in one agent's static config that also appear
|
|
119
|
-
in `known_host_injected` for another — surfaces the exact class of bug
|
|
120
|
-
documented in `docs/research.md` (Codex + mirasim same-name collision)
|
|
121
|
-
*before* it causes a startup failure, not after.
|
|
122
|
-
- Human-readable output matches the table style already used throughout this
|
|
123
|
-
project's manual investigations (✅/⚠️/❌ per row) — this isn't cosmetic,
|
|
124
|
-
it's continuity with how these findings were actually communicated and
|
|
125
|
-
understood during research.
|
|
126
|
-
- `--json` emits `AgentSnapshot[]` for P5's SDK and any future GUI to
|
|
127
|
-
consume without re-parsing terminal output.
|
|
128
|
-
- Exit code is non-zero if any ⚠️/❌ finding exists. `doctor` is meant to be
|
|
129
|
-
runnable in CI/pre-commit, not just read by a human.
|
|
130
|
-
|
|
131
|
-
### P0 acceptance
|
|
132
|
-
|
|
133
|
-
Run against *this* machine (which has known, already-diagnosed issues
|
|
134
|
-
documented in `docs/research.md`) and confirm `doctor` independently
|
|
135
|
-
rediscovers at least these three already-known findings without being told
|
|
136
|
-
where to look:
|
|
137
|
-
1. The Codex `openspec-*` skill duplication (already fixed — confirm it now
|
|
138
|
-
reports clean).
|
|
139
|
-
2. Any current MCP server name collision against `known_host_injected`.
|
|
140
|
-
3. Any skill file with incorrect case (currently none — confirm it reports
|
|
141
|
-
clean, and separately unit-test the detector against a synthetic
|
|
142
|
-
`skill.md`).
|
|
143
|
-
|
|
144
|
-
If `doctor` can't independently reproduce findings that were previously only
|
|
145
|
-
found by hand, it isn't done — restating `docs/research.md`'s conclusions as
|
|
146
|
-
hardcoded checks doesn't count.
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## P1 — `trellis sync skills` / `trellis sync instructions`
|
|
151
|
-
|
|
152
|
-
> **Done and archived** (`openspec/changes/archive/2026-09-12-trellis-sync-p1/`).
|
|
153
|
-
> Section kept as a historical note only — see `docs/roadmap.md` for the
|
|
154
|
-
> actual outcome and the two real bugs the acceptance pass caught. Don't
|
|
155
|
-
> edit this section further.
|
|
156
|
-
|
|
157
|
-
Depends on P0's `AgentSnapshot` to decide *what* needs syncing; this phase
|
|
158
|
-
adds the *write* path.
|
|
159
|
-
|
|
160
|
-
- `.trellis/` canonical source becomes real for the first time here — not
|
|
161
|
-
before. `src/core/canonical.ts`: `loadCanonicalSource(): CanonicalSource`
|
|
162
|
-
reads `~/.trellis/skills/*/SKILL.md`, `~/.trellis/agents/*.md`,
|
|
163
|
-
`~/.trellis/agents.md`, `~/.trellis/scope.yaml`. **Global only** — see
|
|
164
|
-
`docs/architecture.md` "Global vs. workspace scope". No `root` parameter,
|
|
165
|
-
no workspace merge; don't build toward the `.agents Protocol` draft's
|
|
166
|
-
global+workspace precedence model until that's an explicit decision, not
|
|
167
|
-
a default carried over from the draft.
|
|
168
|
-
- `src/adapters/claude-code.ts`, `src/adapters/codex.ts`, `src/adapters/kiro.ts`,
|
|
169
|
-
`src/adapters/pi.ts` each implement `TrellisAdapter` (`src/core/adapter.ts`)
|
|
170
|
-
for skills + instructions only — MCP is P2/P4, deliberately kept out of
|
|
171
|
-
this phase's scope. Pi's adapter is the same symlink shape as the other
|
|
172
|
-
three (`~/.pi/agent/skills`, `~/.pi/agent/AGENTS.md` — confirmed real,
|
|
173
|
-
persistent paths per design.md D5 in the archived P0 change; see
|
|
174
|
-
docs/architecture.md's corrected pi adapter notes) — P4 is only pi's MCP
|
|
175
|
-
bridge, not its skills/instructions, which need nothing more than what
|
|
176
|
-
every other agent here needs.
|
|
177
|
-
`plan()` filters skills/subagent profiles through `isInScope` before
|
|
178
|
-
producing any plan item — a skill scoped away from that adapter's `id`
|
|
179
|
-
must produce zero plan items for it, not a plan item that apply() later
|
|
180
|
-
skips. `apply()` handles three cases, not just creation:
|
|
181
|
-
- **create/repair**: no symlink, or a symlink pointing at the wrong
|
|
182
|
-
target — safe to (re)write.
|
|
183
|
-
- **remove**: an agent's skill directory has a Trellis-managed symlink
|
|
184
|
-
(its realpath resolves inside the canonical `skills/` root) whose
|
|
185
|
-
corresponding skill either no longer exists in canonical or was just
|
|
186
|
-
scoped away from this agent — this is the "delete" half of "add once,
|
|
187
|
-
remove once, reaches every agent," and it's easy to build only the
|
|
188
|
-
create path and quietly never finish this half.
|
|
189
|
-
- **refuse**: the path exists as a *real* directory, not a symlink at
|
|
190
|
-
all — this is not Trellis's to touch. Surface it as a conflict and
|
|
191
|
-
stop; never delete something that might be a user's own content just
|
|
192
|
-
because its name matches a skill Trellis also knows about.
|
|
193
|
-
|
|
194
|
-
The realpath check in the remove case is not optional: it's what makes
|
|
195
|
-
removal safe. Only ever delete a symlink Trellis can prove it created
|
|
196
|
-
(points inside the canonical source), never a same-named real directory.
|
|
197
|
-
- `verify()` re-runs the relevant P0 probe and diffs against canonical —
|
|
198
|
-
this is why P0's probes are written as reusable functions, not
|
|
199
|
-
doctor-command-only logic.
|
|
200
|
-
|
|
201
|
-
### P1 acceptance
|
|
202
|
-
|
|
203
|
-
Point at a scratch `$HOME` (or a container) with none of the four agents'
|
|
204
|
-
skill directories populated, run `trellis sync skills`, then run `trellis
|
|
205
|
-
doctor` and confirm zero findings. Then manually corrupt one symlink
|
|
206
|
-
(point it somewhere wrong) and confirm `doctor` catches it before re-running
|
|
207
|
-
sync to confirm `apply()` repairs it idempotently. Separately: add one
|
|
208
|
-
skill scoped to `[claude-code]` only in `scope.yaml`, run `trellis sync
|
|
209
|
-
skills`, and confirm it appears only in Claude Code's skills directory —
|
|
210
|
-
not Codex's, Kiro's, or pi's.
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## P2 — `trellis mcp sync`
|
|
215
|
-
|
|
216
|
-
> **Done and archived** (`openspec/changes/archive/2026-09-12-trellis-mcp-sync-p2/`).
|
|
217
|
-
> Section kept as a historical note only — see `docs/roadmap.md` for the
|
|
218
|
-
> actual outcome. Two corrections against this plan's own assumptions,
|
|
219
|
-
> found before/while implementing: neither `@iarna/toml` nor `smol-toml`
|
|
220
|
-
> survives a round-trip (both drop comments, reformat arrays) — Codex is
|
|
221
|
-
> patched by a hand-rolled line-based section locator/splicer instead
|
|
222
|
-
> (`src/lib/tomlSection.ts`), no library dependency at all; and automatic
|
|
223
|
-
> MCP removal turned out to be unsafe without an ownership marker a bare
|
|
224
|
-
> TOML/JSON key has no equivalent of (a symlink's realpath proves it for
|
|
225
|
-
> skills) — deferred until a lock-file mechanism exists, `plan()` only
|
|
226
|
-
> ever produces create/repair + conflict for MCP. Don't edit this section
|
|
227
|
-
> further.
|
|
228
|
-
|
|
229
|
-
The highest-risk phase — this is where `docs/research.md`'s hardest-won
|
|
230
|
-
constraint applies directly: **Codex's adapter must patch a TOML section in
|
|
231
|
-
place, never regenerate the whole file**, because Codex's own `config.toml`
|
|
232
|
-
holds settings Trellis has no business touching (models, trust levels), and
|
|
233
|
-
because mirasim writes to the same file independently.
|
|
234
|
-
|
|
235
|
-
- `src/adapters/codex.ts` (extended): needs a real TOML parser that
|
|
236
|
-
preserves comments/formatting outside the touched section (`@iarna/toml`
|
|
237
|
-
or `smol-toml` — evaluate both for round-trip fidelity before picking one;
|
|
238
|
-
this is exactly the kind of "don't reinvent" call `docs/research.md`
|
|
239
|
-
already commits to). Patch strategy: locate `[mcp_servers.<name>]` by
|
|
240
|
-
section header, replace only that block, leave every byte outside it
|
|
241
|
-
untouched.
|
|
242
|
-
- Collision check runs `known_host_injected` from `mcp/servers.yaml` against
|
|
243
|
-
every server name about to be written; refuses to write and exits non-zero
|
|
244
|
-
on collision, with the exact error class from `docs/research.md`
|
|
245
|
-
(`url is not supported for stdio`) quoted in the failure message so a user
|
|
246
|
-
hitting this via Trellis gets the real cause immediately instead of
|
|
247
|
-
rediscovering it as a mysterious Codex crash.
|
|
248
|
-
- Every adapter's `plan()` filters servers through `isInScope` using each
|
|
249
|
-
server's inline `agents:` field (not `scope.yaml` — see
|
|
250
|
-
`docs/architecture.md` "Private / agent-specific capabilities" for why
|
|
251
|
-
MCP scoping is inline) before generating anything for that agent.
|
|
252
|
-
- `src/adapters/claude-code.ts` / `kiro.ts` (extended): plain JSON deep-merge
|
|
253
|
-
under `mcpServers`, values always emitted as `${VAR}` — never a literal,
|
|
254
|
-
enforced by a check against `secrets.policy.yaml`'s `reject_patterns`
|
|
255
|
-
*before* the write happens, not just audited after (P3 audits adapter
|
|
256
|
-
*output*; this is a pre-write guard specific to the MCP adapter, cheaper
|
|
257
|
-
to catch here than after the fact).
|
|
258
|
-
- **Hub mode branch** (`docs/architecture.md` "MCP hub mode"): every
|
|
259
|
-
adapter checks `canonical.mcp.hub` first. If set, skip the whole
|
|
260
|
-
N-server merge/patch logic above and instead ensure exactly one static
|
|
261
|
-
entry (name TBD, e.g. `trellis-hub`) pointing at `hub.url` exists —
|
|
262
|
-
Codex still patches in place, Claude/Kiro still JSON-merge, just one
|
|
263
|
-
entry instead of N. The collision check still runs, just against that
|
|
264
|
-
one name instead of every server name.
|
|
265
|
-
|
|
266
|
-
### P2 acceptance
|
|
267
|
-
|
|
268
|
-
Reproduce the exact Codex incident from `docs/research.md` in a sandboxed
|
|
269
|
-
`config.toml` (a stdio server statically defined, then a same-name `url`
|
|
270
|
-
server "injected" via `-c` the way mirasim does) and confirm Trellis's
|
|
271
|
-
collision check refuses the write *before* attempting it, rather than
|
|
272
|
-
letting Codex fail to start. Separately, with `hub.url` set in the fixture's
|
|
273
|
-
`servers.yaml`, confirm each agent's generated config contains exactly one
|
|
274
|
-
MCP entry regardless of how many servers are defined.
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## P3 — `trellis secrets audit`
|
|
279
|
-
|
|
280
|
-
> **Done and archived** (`openspec/changes/archive/2026-09-12-trellis-secrets-audit-p3/`).
|
|
281
|
-
> Section kept as a historical note only — see `docs/roadmap.md` for the
|
|
282
|
-
> actual outcome. Built as planned, with no dependency added: env-var-name
|
|
283
|
-
> extraction turned out to need only real `JSON.parse` (Claude Code/Kiro)
|
|
284
|
-
> and a five-line regex over one TOML line shape (Codex), not a general
|
|
285
|
-
> parser or a reused write-path module. Don't edit this section further.
|
|
286
|
-
|
|
287
|
-
- `src/commands/secretsAudit.ts`: reads every adapter's *actual output file*
|
|
288
|
-
(not the canonical source — the point is catching what actually landed on
|
|
289
|
-
disk) and runs `secrets.policy.yaml`'s `reject_patterns` against it.
|
|
290
|
-
- Must catch, as regression tests, the two real incidents already on
|
|
291
|
-
record: a GitLab PAT stored under the wrong variable name (this is a
|
|
292
|
-
*naming* bug the pattern-match alone won't catch — audit also needs an
|
|
293
|
-
"unexpected variable name" check against `allowed_vars`, not just a
|
|
294
|
-
literal-value regex) and a literal token value embedded directly in a
|
|
295
|
-
generated config (the regex case).
|
|
296
|
-
|
|
297
|
-
## P4 — pi MCP bridge extension
|
|
298
|
-
|
|
299
|
-
> **Done and archived** (`openspec/changes/archive/2026-09-12-trellis-pi-mcp-bridge-p4/`).
|
|
300
|
-
> Section kept as a historical note only — see `docs/roadmap.md` for the
|
|
301
|
-
> actual outcome. Built largely as sketched (a symlinked extension, no
|
|
302
|
-
> settings.json write, `registerTool` per MCP tool), but the delivered
|
|
303
|
-
> file had to become a fully bundled, dependency-free build output
|
|
304
|
-
> (`dist/pi-bridge/bundle.js` via `esbuild`) rather than raw TypeScript
|
|
305
|
-
> source — a real sandbox run against the actual `pi` binary showed a
|
|
306
|
-
> symlinked file's own imports resolve relative to the symlink's path,
|
|
307
|
-
> not its target, so an unbundled file could never resolve its
|
|
308
|
-
> dependencies once placed in a real user's home directory. Don't edit
|
|
309
|
-
> this section further.
|
|
310
|
-
|
|
311
|
-
**MCP only** — pi's skills/instructions adapter is P1's `src/adapters/pi.ts`
|
|
312
|
-
(same symlink shape as every other agent, see P1 above); P4 is exclusively
|
|
313
|
-
the MCP bridge, since pi has no native MCP client at all (docs/research.md).
|
|
314
|
-
|
|
315
|
-
§0.2's investigation resolved during P0 (design.md D5 in the archived
|
|
316
|
-
`trellis-doctor-p0` change): pi has a real discovery directory
|
|
317
|
-
(`~/.pi/agent/skills`), settling which of the two candidate shapes below
|
|
318
|
-
applies — the first one:
|
|
319
|
-
|
|
320
|
-
- A pi extension package, installed once, that on startup reads
|
|
321
|
-
`.trellis/mcp/servers.yaml`. If `mcp.hub` is unset, it opens an
|
|
322
|
-
`@modelcontextprotocol/sdk` `StdioClientTransport` per server and calls
|
|
323
|
-
pi's `registerTool` for each tool the server reports; if `mcp.hub.url` is
|
|
324
|
-
set, it instead opens a single `StreamableHTTPClientTransport` to that
|
|
325
|
-
URL — one connection instead of N processes to keep alive, see
|
|
326
|
-
`docs/architecture.md` "MCP hub mode".
|
|
327
|
-
|
|
328
|
-
This is the one adapter that is genuine runtime code, not a config
|
|
329
|
-
generator — budget real testing time against a live pi session, not just
|
|
330
|
-
unit tests of the bridging logic in isolation.
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
|
|
334
|
-
## Sequencing note
|
|
335
|
-
|
|
336
|
-
Build order: **Claude Code → Codex → Kiro → pi**, in that order, for every
|
|
337
|
-
phase through P4. Claude Code and Codex are already the best-understood
|
|
338
|
-
(most of `docs/research.md`'s hard-won findings are about them); Kiro is
|
|
339
|
-
structurally identical to Claude Code and should be fast once that shape is
|
|
340
|
-
validated; pi is last because it has the one open unknown (§0.2) and the one
|
|
341
|
-
genuinely novel engineering problem (§P4).
|