@tendrilapp/cli 0.1.47 → 0.1.49

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 CHANGED
@@ -263,6 +263,11 @@ Batch runs (several components in one session):
263
263
  say so — but the host then re-implements the partner's pixels
264
264
  locally instead of composing the verified partner bundle, so
265
265
  surface the choice before you spend the rounds.
266
+ Discovery only sees recording sets in the SCANNED roots: the set's
267
+ parent directory by default (bounded), or `--library <dir>`. Sets
268
+ in another project root are invisible to pairing until co-located
269
+ or pointed at — the tools name their scanned roots so "not
270
+ scanned" never reads as "no relationship".
266
271
 
267
272
  ## Generating (the agent-harness engine — you are the proposer)
268
273
 
@@ -388,15 +393,25 @@ on disk — or `null` where a scoring path produced a score with no
388
393
  pixels, which is a real outcome and not a bug.
389
394
 
390
395
  The verdict, the pixels and the diff-colour legend join into one offline
391
- sheet at `verify-evidence/inspect.html`. That sheet comes from the CLI
392
- and has no MCP tool of its own, so OFFER to run
393
- `npx @tendrilapp/cli inspect <bundle-dir>` rather than asking the user
394
- to type it, and say what it produces before you run it.
396
+ sheet at `verify-evidence/inspect.html`. A GREEN verify writes that
397
+ sheet itself (the report's `eyeCheck.sheetBuilt` says so) report its
398
+ path, never ask whether to produce it. After a FAILED verify, run
399
+ `npx @tendrilapp/cli inspect <bundle-dir>` yourself before diagnosing:
400
+ the sheet is free, offline and account-less, so no cost or risk exists
401
+ that would justify a prompt in front of it. Note the ORDER — `inspect`
402
+ reads the evidence a `verify` run wrote, so it runs after `verify`,
403
+ never before.
395
404
 
396
405
  Point the user at that sheet, not at terminal scrollback: a verdict
397
406
  nobody can re-read beside the evidence it describes cannot be handed to
398
- anyone. Note the ORDER — `inspect` writes crops into the evidence
399
- directory it documents, so run it after `verify`, never before.
407
+ anyone.
408
+
409
+ Verdict caveats and coverage lines (`INCOMPLETE`, `PIXEL-ONLY`,
410
+ `coverage-denominator-unknown`, `composition-not-checked`) are
411
+ DISCLOSURES, not questions: nothing is blocked and no answer is
412
+ required. State them as plain findings in your final report — never
413
+ convert one into an options dialog. A verified run's happy path has
414
+ ZERO questions.
400
415
 
401
416
  NEVER re-score a bundle against a DIFFERENT recording set than the one
402
417
  it is bound to — scoring rewrites the bundle's verification identity
@@ -408,13 +423,74 @@ denominator — if it prints INCOMPLETE with unrecorded poses, the
408
423
  component is not done: record the missing poses (re-running plan tops
409
424
  up sets recorded under the old sampled default).
410
425
 
426
+ ## Publishing — the terminal step of a green run
427
+
428
+ A verified component's value lands when it is LIVE. This is an
429
+ invariant, not a preference: every run that reaches a green verify ends
430
+ with the user holding the live portal URL — or, if the first-publish
431
+ approval is still pending, unmistakably looking at the approve action.
432
+ No success path terminates with the component reachable only on local
433
+ disk. Do not ask permission to enter this sequence — the human gate is
434
+ the browser approval itself, which only the user can give and which no
435
+ machine token can fake.
436
+
437
+ The terminal sequence on green: `verify` → the inspect sheet (written
438
+ automatically) → `tendril_publish` → surface what it returns:
439
+
440
+ - `status: "approval-pending"` (a component's FIRST publish): relay the
441
+ `approveUrl` VERBATIM and name the `approveAsAccount` it carries —
442
+ state the account explicitly even when it looks obvious, because the
443
+ approving browser can be signed in to a different account than this
444
+ session runs under, and a mismatch strands the user on an empty
445
+ approve page. Do not urge them to approve: approving includes
446
+ accepting the design system's publishing terms, and that is their
447
+ decision to read and make.
448
+ - Then `tendril_publish_wait`, in a loop: each call waits about a
449
+ minute and returns either the outcome or `approval-pending` again.
450
+ A pending return is a HEARTBEAT, not a failure — keep calling while
451
+ `remainingSeconds` says the request is live, relaying a brief
452
+ liveness line roughly every few minutes (each return carries
453
+ `waitedTotalSeconds`/`remainingSeconds`; you need not narrate every
454
+ heartbeat). Never describe an in-flight or pending wait as stuck —
455
+ a decided approval continues straight into upload and commit inside
456
+ the same call, which can take minutes with no visible progress —
457
+ and never abandon the loop while the request is live unless the
458
+ user redirects you. A denial, a lapse and success each arrive as
459
+ their own sentence — report the one you got.
460
+ - A re-publish of an already-published component completes in one call,
461
+ no approval round trip — so it has NO browser gate. Announce in one
462
+ line what is about to go live BEFORE the call ("publishing the
463
+ updated Checkbox to your portal"); that line is the human-visible
464
+ moment for a step no click will confirm.
465
+ - Not signed in? Surface `tendril_login` first — the sequence pauses at
466
+ sign-in; it never silently skips publishing.
467
+
468
+ Sub-bar runs do NOT publish — the CLI refuses a declined run, and the
469
+ honest failure report is that run's correct terminal state. The
470
+ invariant is scoped to success.
471
+
472
+ Make the finish legible: the FIRST line of your final report is "done —
473
+ live at <URL>" (or "done — waiting for your Approve at <link>, as
474
+ <account>"), before scores, caveats and file lists. The user should
475
+ never have to ask where their component is.
476
+
477
+ A green verify may also print COMPOSE lines: other recorded components
478
+ that embed the one just verified. Relay them after the done-line as
479
+ DISCLOSURES (zero questions — the confirm decision is human-only, in
480
+ the user's terminal, with the exact commands the lines already carry),
481
+ and never confirm or decline a pairing yourself.
482
+
411
483
  ## Code Connect (extra value, after verify passes)
412
484
 
413
485
  `tendril_codeconnect` emits a Figma Code Connect template (.figma.ts)
414
486
  for a certified bundle: every Figma variant value mapped to its
415
- verified prop fragment, stamped with the trust statement. Offer it
416
- when the user's team is on a Figma Organization/Enterprise plan (Code
417
- Connect is unavailable below those). You need the component set's
487
+ verified prop fragment, stamped with the trust statement. This is
488
+ NEVER a dialog on the happy path (the zero-questions rule): mention it
489
+ in ONE line of the final report, AFTER the done-and-live line ("Code
490
+ Connect template available — say the word"), and only act on it when
491
+ the user takes it up. It applies when the user's team is on a Figma
492
+ Organization/Enterprise plan (Code Connect is unavailable below
493
+ those). You need the component set's
418
494
  figma.com URL (node-id included). Publishing is the USER'S action with
419
495
  their token — `npx @figma/code-connect connect publish` — or, if this
420
496
  session has the Figma MCP's code-connect write tools, offer to publish
@@ -131,12 +131,12 @@ var TOOLS = [
131
131
  },
132
132
  {
133
133
  name: "tendril_publish_wait",
134
- description: "Phase two of the browser-approved publish: waits (with live progress) for the user's Approve click on the page tendril_publish returned, then uploads the bundle, commits it, and returns the live publication URL. Call it right after relaying the approve link. A denial, a lapse, and success each come back as their own sentence \u2014 report the outcome and the URL to the user.",
134
+ description: 'Phase two of the browser-approved publish. Waits in a BOUNDED window (~1 minute per call) for the user\'s Approve click on the page tendril_publish returned, then uploads the bundle, commits it, and returns the live publication URL. Call it right after relaying the approve link. While the human has not decided, each call returns `status: "approval-pending"` \u2014 that is a heartbeat, not a failure: tell the user in one short line that you are still waiting (restate the approve link and the account to approve as, ONLY if they seem lost; never urge the decision), then call this again to keep waiting. The request stays live for ~30 minutes. A denial, a lapse, and success each come back as their own sentence \u2014 report the outcome, and on success lead with the live URL. A decided approval continues straight into upload and commit INSIDE the same call \u2014 that phase can take minutes and may render no progress in some hosts; that is normal, not stuck. Some hosts render no progress at all during a call; the bounded window IS the liveness, so never describe an in-flight call as stuck \u2014 and a brief liveness line roughly every few minutes is enough, you need not narrate every heartbeat (each pending return carries waitedTotalSeconds/remainingSeconds to say where the wait stands).',
135
135
  schema: z.object({
136
136
  bundleDir: str("the same bundle directory tendril_publish was called with"),
137
137
  portal: optStr("portal origin override (must match tendril_publish's)")
138
138
  }),
139
- argv: (i) => ["publish", i["bundleDir"], "--approve-wait", ...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []]
139
+ argv: (i) => ["publish", i["bundleDir"], "--approve-wait", "--wait-window", "55", ...typeof i["portal"] === "string" ? ["--to", i["portal"]] : []]
140
140
  },
141
141
  {
142
142
  name: "tendril_record_next",
@@ -298,7 +298,7 @@ var TOOLS = [
298
298
  },
299
299
  {
300
300
  name: "tendril_engine_score",
301
- description: "AGENT-HARNESS engine, step 2 (the oracle): scores a candidate bundle directory against recorded truth \u2014 per-config pixels, behaviors, state parity (recording-selected \u2014 a bundle cannot unschedule it) \u2014 and returns feedback plus evidence artifacts. Iterate until allPass or two non-improving rounds. Only THIS tool's output counts as a score; never claim numbers yourself.",
301
+ description: "AGENT-HARNESS engine, step 2 (the oracle): scores a candidate bundle directory against recorded truth \u2014 per-config pixels, behaviors, state parity (recording-selected \u2014 a bundle cannot unschedule it) \u2014 and returns feedback plus evidence artifacts. Iterate until allPass or two non-improving rounds. Only THIS tool's output counts as a score; never claim numbers yourself. allPass ends the LOOP, not the run: finish with tendril_verify, and a green verify's terminal step is publishing \u2014 the component is done when it is LIVE, not when it scores.",
302
302
  schema: z.object({
303
303
  taskOrSet: str("reference task name or recording-set directory"),
304
304
  candidateDir: str("directory containing the proposed bundle files"),
@@ -341,7 +341,7 @@ var TOOLS = [
341
341
  },
342
342
  {
343
343
  name: "tendril_verify",
344
- description: "Verify/check that a component matches its Figma design \u2014 use when the user asks whether an implementation is faithful to the design, or to re-certify an existing Tendril bundle. Recomputes full verification (per-config status, behaviors, composition, evidence artifacts). Free, account-less, network-less \u2014 the trust anchor. Exit 5 means below the target bar with an honest report \u2014 sub-bar scores, or at bar cert a config demoted by absent-ink clusters.",
344
+ description: "Verify/check that a component matches its Figma design \u2014 use when the user asks whether an implementation is faithful to the design, or to re-certify an existing Tendril bundle. Recomputes full verification (per-config status, behaviors, composition, evidence artifacts). Free, account-less, network-less \u2014 the trust anchor. Exit 5 means below the target bar with an honest report \u2014 sub-bar scores, or at bar cert a config demoted by absent-ink clusters. A GREEN run writes the inspect sheet itself and returns `next`: its terminal step is publishing (tendril_publish \u2192 tendril_publish_wait) \u2014 a verified component reachable only on local disk is an unfinished run; a sub-bar run never publishes.",
345
345
  schema: z.object({
346
346
  bundleDir: str("bundle directory to verify"),
347
347
  bar: optStr("pass (default) or cert"),
@@ -494,7 +494,7 @@ var IMPLEMENT_PROMPT = {
494
494
  build: (figmaUrl) => [
495
495
  `Implement the Figma component at ${figmaUrl} as a pixel-verified React component using the Tendril pipeline.`,
496
496
  "- If the tendril skill (or plugin) is installed, load it and follow it.",
497
- "- Otherwise: start at tendril_record_plan; while recording, make exactly the Figma call each tendril_record_next step names (get_metadata comes before any get_design_context) and pass every response VERBATIM to the ingest tools (no files to save \u2014 the parts params take the response blocks directly); then tendril_engine_brief, implement the candidate, and tendril_engine_score until the bar passes; finish with tendril_verify.",
497
+ "- Otherwise: start at tendril_record_plan; while recording, make exactly the Figma call each tendril_record_next step names (get_metadata comes before any get_design_context) and pass every response VERBATIM to the ingest tools (no files to save \u2014 the parts params take the response blocks directly); then tendril_engine_brief, implement the candidate, and tendril_engine_score until the bar passes; finish with tendril_verify \u2014 and on green, publish (tendril_publish, then tendril_publish_wait until decided): the run ends at the live URL or the pending approve action, never at local disk.",
498
498
  "- Verdicts come only from Tendril's scores \u2014 never claim or estimate fidelity numbers yourself."
499
499
  ].join("\n")
500
500
  };
@@ -521,7 +521,11 @@ for (const tool of TOOLS) {
521
521
  const onProgress = token === void 0 || extra?.sendNotification === void 0 ? void 0 : (p) => {
522
522
  void extra.sendNotification({
523
523
  method: "notifications/progress",
524
- params: { progressToken: token, progress: p.done, total: p.total, message: `scored ${p.done}/${p.total} configs (${p.label})` }
524
+ // The label is the CLI's own sentence for THIS phase
525
+ // scoring, uploading, waiting for an approval. A
526
+ // hardcoded "scored N/M configs" here made every
527
+ // non-scoring tool narrate someone else's work.
528
+ params: { progressToken: token, progress: p.done, total: p.total, message: `${p.label} (${p.done}/${p.total})` }
525
529
  });
526
530
  };
527
531
  return toolResult(await runCli(tool.argv(input), 9e5, onProgress));