@tendrilapp/cli 0.1.49 → 0.1.51
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/SKILL.md +57 -4
- package/dist/tendril-mcp.js +51 -0
- package/dist/tendril.js +2836 -1233
- package/package.json +1 -1
package/dist/SKILL.md
CHANGED
|
@@ -254,11 +254,20 @@ Batch runs (several components in one session):
|
|
|
254
254
|
lists unconfirmed partner pairs, this recording references OTHER
|
|
255
255
|
recorded components (a segmented control built from a recorded
|
|
256
256
|
Control, a dialog holding a recorded Button). Order matters:
|
|
257
|
-
generate the PARTNER bundle first, then
|
|
258
|
-
|
|
257
|
+
generate the PARTNER bundle first, then put the pairing decision in
|
|
258
|
+
front of the human — it is human-only on either channel, and you
|
|
259
|
+
can never make it. CLICK PATH (lead with this): `tendril_compose`
|
|
260
|
+
puts the pairing in their browser as an Approve card (sign in first
|
|
261
|
+
via `tendril_login` if needed — one browser Approve each); relay
|
|
262
|
+
the approve link VERBATIM, name the account the result says to
|
|
263
|
+
approve as, never urge, then loop `tendril_compose_wait` (same
|
|
264
|
+
heartbeat contract as publish waits: pending is a heartbeat, one
|
|
265
|
+
short liveness line, never "stuck"). The card's Deny is NOT NOW —
|
|
266
|
+
this request only, never a permanent decline. TERMINAL PATH: the
|
|
267
|
+
human runs
|
|
259
268
|
`npx -y -p @tendrilapp/cli@latest tendril compose --set <host-set-dir>`
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
themselves (confirm or `--decline <pair-key>`; decline there IS
|
|
270
|
+
permanent — asked once). Never confirm on either channel yourself.
|
|
262
271
|
Generating the host with pairs undecided is valid — the brief will
|
|
263
272
|
say so — but the host then re-implements the partner's pixels
|
|
264
273
|
locally instead of composing the verified partner bundle, so
|
|
@@ -268,6 +277,41 @@ Batch runs (several components in one session):
|
|
|
268
277
|
in another project root are invisible to pairing until co-located
|
|
269
278
|
or pointed at — the tools name their scanned roots so "not
|
|
270
279
|
scanned" never reads as "no relationship".
|
|
280
|
+
CONNECTING COMPONENTS THAT ALREADY EXIST (both recorded, both
|
|
281
|
+
generated): step ONE is putting both recording sets in one scanned
|
|
282
|
+
root (co-locate, or --library). If a set is not on this machine at
|
|
283
|
+
all — a fresh checkout, a different laptop, a component a teammate
|
|
284
|
+
published — do NOT conclude the work needs the original machine:
|
|
285
|
+
`tendril_pull <component name>` downloads the recording set the
|
|
286
|
+
published verdict was measured against, into
|
|
287
|
+
`./recordings/<component>/`. One call per component; the name is
|
|
288
|
+
exact, and a name that does not resolve comes back with the
|
|
289
|
+
account's real component names to pick from. It needs a portal
|
|
290
|
+
session (`tendril_login`) because a recording set belongs to the
|
|
291
|
+
account that published it — everything after the pull is local and
|
|
292
|
+
account-less as always. A publication published before recording
|
|
293
|
+
sets travelled, or published with `--no-recordings`, says so: that
|
|
294
|
+
is an honest stop, not a retry. If the pairing then shows as
|
|
295
|
+
NAME-ONLY (the host was recorded over MCP, so Figma's instance→
|
|
296
|
+
component bindings were never captured), run
|
|
297
|
+
`tendril_record_bindings` on the HOST set — one or two batched
|
|
298
|
+
REST calls, congruence-verified, no re-recording; needs the Figma
|
|
299
|
+
connect — if absent, that is ONE more browser Allow via
|
|
300
|
+
`tendril_figma_connect`, name it up front so it never reads as a
|
|
301
|
+
detour — and the pairing becomes id-backed and confirmable. Then
|
|
302
|
+
the click path above (tendril_compose → Approve →
|
|
303
|
+
tendril_compose_wait). After the approve: the HOST bundle is
|
|
304
|
+
REGENERATED with the same --library roots (a verified bundle is
|
|
305
|
+
frozen — composition never retrofits one; the partner bundle is
|
|
306
|
+
reused verbatim). That regeneration is a normal generation run:
|
|
307
|
+
ask the model question there as always, and set the expectation in
|
|
308
|
+
one line that it takes a few scored attempts. If the regenerated
|
|
309
|
+
host was ALREADY published, the republish completes in one
|
|
310
|
+
announced call; if it was NEVER published, a SECOND, different
|
|
311
|
+
browser card appears — the publish approval — say so before it
|
|
312
|
+
does. On session resume, a pending connect decision may be waiting:
|
|
313
|
+
if `pending-compose.json` exists under the user config dir, run
|
|
314
|
+
`tendril_compose_wait` first.
|
|
271
315
|
|
|
272
316
|
## Generating (the agent-harness engine — you are the proposer)
|
|
273
317
|
|
|
@@ -464,6 +508,15 @@ automatically) → `tendril_publish` → surface what it returns:
|
|
|
464
508
|
moment for a step no click will confirm.
|
|
465
509
|
- Not signed in? Surface `tendril_login` first — the sequence pauses at
|
|
466
510
|
sign-in; it never silently skips publishing.
|
|
511
|
+
- A publish also carries the RECORDING SET the verdict was measured
|
|
512
|
+
against, so the work can be picked up on any machine the user signs
|
|
513
|
+
in from (`tendril_pull`). It is private to their account — no share
|
|
514
|
+
link and no public page reaches it — and it rides the same deletion
|
|
515
|
+
as everything else. If the publish result says it went without the
|
|
516
|
+
recording set, relay that line as-is: it names why, and it is never
|
|
517
|
+
a failed publish. The first time an account carries one, the portal
|
|
518
|
+
asks for one browser Accept naming the new custody; relay that link
|
|
519
|
+
the way you relay an approve link, and never urge the decision.
|
|
467
520
|
|
|
468
521
|
Sub-bar runs do NOT publish — the CLI refuses a declined run, and the
|
|
469
522
|
honest failure report is that run's correct terminal state. The
|
package/dist/tendril-mcp.js
CHANGED
|
@@ -138,6 +138,23 @@ var TOOLS = [
|
|
|
138
138
|
}),
|
|
139
139
|
argv: (i) => ["publish", i["bundleDir"], "--approve-wait", "--wait-window", "55", ...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []]
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
name: "tendril_pull",
|
|
143
|
+
description: "Bring a published component's RECORDING SET onto this machine \u2014 the recorded design truth its verdict was measured against. Use it when the recordings are not here: a fresh machine, a clone with no recordings/ directory, or a component someone else on the team published. ONE bounded call, no wait twin. Needs a portal session (tendril_login) because a recording set belongs to the account that published it; everything afterwards \u2014 verify, compose, republish \u2014 is local, offline and account-less as always. The name is EXACT and the portal's refusals list the user's real component names, so a name that does not resolve is a one-step correction rather than a dead end; when one name spans two design systems, pass `figmaFile`. It writes to ./recordings/<component>/ and never overwrites a different set already there. AFTER pulling, call tendril_record_status on each set you pulled, then continue the connect prompt from its step 3.",
|
|
144
|
+
schema: z.object({
|
|
145
|
+
component: str("the component name, exactly as the user's library shows it"),
|
|
146
|
+
figmaFile: optStr("the design system's Figma file key \u2014 only when one name spans several of the user's design systems (the portal's refusal says when)"),
|
|
147
|
+
dest: optStr("where the set lands (default: ./recordings/<component>)"),
|
|
148
|
+
portal: optStr("portal origin override for self-hosted portals (defaults to the stored session's portal)")
|
|
149
|
+
}),
|
|
150
|
+
argv: (i) => [
|
|
151
|
+
"pull",
|
|
152
|
+
i["component"],
|
|
153
|
+
...typeof i["figmaFile"] === "string" ? ["--figma-file", i["figmaFile"]] : [],
|
|
154
|
+
...typeof i["dest"] === "string" ? ["--dest", i["dest"]] : [],
|
|
155
|
+
...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []
|
|
156
|
+
]
|
|
157
|
+
},
|
|
141
158
|
{
|
|
142
159
|
name: "tendril_record_next",
|
|
143
160
|
annotations: { readOnlyHint: true },
|
|
@@ -151,6 +168,40 @@ var TOOLS = [
|
|
|
151
168
|
schema: z.object({ setDir: str("recording set directory") }),
|
|
152
169
|
argv: (i) => ["record", "rest-fetch", "--set", i["setDir"]]
|
|
153
170
|
},
|
|
171
|
+
{
|
|
172
|
+
name: "tendril_record_bindings",
|
|
173
|
+
description: "Fetch Figma's instance\u2192component bindings for an EXISTING MCP-recorded set \u2014 one or two batched REST calls, no re-recording, pixels/geometry/identity untouched. This is how components that are ALREADY recorded and generated become connectable: the bindings make cross-component pairing id-backed, so `tendril compose` can propose it for the human's confirm. Every binding is congruence-verified against the recorded metadata (a design that changed since recording refuses per pose, named). The CLI makes the requests itself with the user's connected Figma credential (tendril_figma_connect); you make NO Figma call. The result names the next step \u2014 including when no partner set is visible in the scanned root (co-locate the sets or pass compose --library). For sets recorded before file identity was captured, pass the design's file key as `file`.",
|
|
174
|
+
schema: z.object({ setDir: str("recording set directory"), file: optStr("the design's file key (figma.com/design/<KEY>/\u2026) \u2014 only for sets whose manifest lacks figmaFile; an operator assertion, congruence still gates every binding") }),
|
|
175
|
+
argv: (i) => ["record", "bindings", "--set", i["setDir"], ...typeof i["file"] === "string" ? ["--file", i["file"]] : []]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "tendril_compose",
|
|
179
|
+
description: "Put ONE composition pairing in front of the user's BROWSER \u2014 phase one of the browser-approved connect (invariant-5 migration; the terminal `--confirm-compositions` path remains for humans at a TTY). Requires a portal session (tendril_login \u2014 one browser Approve). Returns the approve-page link: RELAY IT verbatim, name the account the result says to approve as, and never urge the decision \u2014 the card carries the engine's honest disclosures and the human reads them. Deny on the card is NOT NOW (this request only, never a permanent decline). You cannot decide this yourself on any channel: the portal accepts the decision only from the user's signed-in browser, and the terminal flag only from an interactive TTY. A host with several open pairings needs `pair` (one card = one decision). Then finish with tendril_compose_wait. Both recording sets must be visible in one scanned workspace \u2014 co-locate them or pass `library`.",
|
|
180
|
+
schema: z.object({
|
|
181
|
+
setDir: str("the HOST recording set directory (the component that embeds the partner)"),
|
|
182
|
+
pair: optStr("the pair-key to request when the host has several open pairings"),
|
|
183
|
+
library: z.array(z.string()).optional().describe("workspace root(s) holding the partner recording set(s) \u2014 required when they live in another project root"),
|
|
184
|
+
portal: optStr("portal origin override (defaults to the stored session's portal)")
|
|
185
|
+
}),
|
|
186
|
+
argv: (i) => [
|
|
187
|
+
"compose",
|
|
188
|
+
"--set",
|
|
189
|
+
i["setDir"],
|
|
190
|
+
"--approve-start",
|
|
191
|
+
...typeof i["pair"] === "string" ? ["--pair", i["pair"]] : [],
|
|
192
|
+
...Array.isArray(i["library"]) ? i["library"].flatMap((d) => ["--library", d]) : [],
|
|
193
|
+
...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "tendril_compose_wait",
|
|
198
|
+
description: 'Phase two of the browser-approved connect. Waits in a BOUNDED window (~1 minute per call) for the user\'s decision on the card tendril_compose returned; on Approve it records the SAME manifest entry the terminal confirm writes \u2014 after re-deriving the pairing from the CURRENT recordings and refusing if anything changed underneath the click (the click is then not wrong; the project moved \u2014 run the connect again). While undecided, each call returns `status: "approval-pending"` \u2014 a heartbeat, not a failure: one short liveness line to the user, then call again; the request stays live ~30 minutes, and a decided card can take moments to land. NOT NOW, a lapse, and success each arrive as their own sentence \u2014 report the one you got. After success: regenerate the HOST bundle with the same library roots (brief \u2192 generate \u2192 score \u2192 verify), then republish \u2014 announce the republish in one line first. Compose waits follow the same heartbeat contract as publish waits: never urge, never call an in-flight wait stuck.',
|
|
199
|
+
schema: z.object({
|
|
200
|
+
setDir: str("the same HOST set directory tendril_compose was called with"),
|
|
201
|
+
portal: optStr("portal origin override (must match tendril_compose's)")
|
|
202
|
+
}),
|
|
203
|
+
argv: (i) => ["compose", "--set", i["setDir"], "--approve-wait", "--wait-window", "55", ...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []]
|
|
204
|
+
},
|
|
154
205
|
{
|
|
155
206
|
name: "tendril_record_fetch",
|
|
156
207
|
description: "Download a Figma asset URL (from get_screenshot's image_url) straight to disk and ingest it as the rep's envelope \u2014 the fallback when only the screenshot piece needs (re-)recording; for a rep's standard three recordings PREFER tendril_record_ingest_rep. Never download the image yourself: the bytes must not pass through your context.",
|