@tendrilapp/cli 0.1.21 → 0.1.24

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 CHANGED
@@ -9,4 +9,12 @@ Figma design systems → verified React components.
9
9
  { "mcpServers": { "tendril": { "command": "tendril-mcp" } } }
10
10
  ```
11
11
 
12
+ Working in Claude Code? Your agent can install the permission
13
+ allowlist for you — it will offer, or ask it to run
14
+ `tendril permissions` (one approval replaces a prompt per call).
15
+
16
+ After every verify, open the eye check: `tendril inspect <bundleDir>`
17
+ renders magnified recorded-vs-rendered crops of every small detail —
18
+ scores measure pixels; the sheet shows shape.
19
+
12
20
  Verification is local, account-less, and network-less — always.
package/dist/SKILL.md CHANGED
@@ -43,13 +43,18 @@ Long generator/score waits: silence is not a health signal, and POLL
43
43
  cadence is not RELAY cadence (run 13: rounds landed 6–11 minutes apart,
44
44
  so a correct 30s poll still left the user in 9-minute silences they
45
45
  flagged as unacceptable). Two duties: relay each round's result the
46
- moment `<candidateDir>/score-history.jsonl` gains a line ("round 3
47
- scored: 15/21 at floor 0.885"), AND when more than ~60 seconds pass
48
- with no round, emit a liveness line from the candidate dir's file
49
- mtimes, which move while the generator is WRITING (thinking pauses are quiet — that is normal, not frozen) ("still
50
- working styles.css touched 20s ago"). The history file APPEARS ONLY
51
- AFTER THE FIRST completed score, so its absence early is normal, not
52
- frozen. A healthy run can take 30+ minutes; never kill on elapsed time
46
+ moment `<candidateDir>/score-history.jsonl` gains a `round-scored` line
47
+ ("round 3 scored: 15/21 at floor 0.885"), AND when more than ~60
48
+ seconds pass with no round, emit a liveness line from the candidate
49
+ dir's file mtimes, which move while the generator is WRITING (thinking
50
+ pauses are quiet that is normal, not frozen) ("still
51
+ working styles.css touched 20s ago"). Every history line carries an
52
+ `event`: `round-start` lands the moment a score BEGINS and has no
53
+ numbers — relay it as "round 3 scoring started", never as a result —
54
+ and `round-scored` carries that round's pass/total/floor/mean. So the
55
+ file appears with the first score's START; before that, its absence
56
+ means scoring has not begun yet, which early on is normal, not frozen.
57
+ A healthy run can take 30+ minutes; never kill on elapsed time
53
58
  alone — count staleness from the LAST CHANGE to any signal, and when
54
59
  past ~20 minutes of true staleness, ask the user before killing.
55
60
 
@@ -57,7 +62,9 @@ Before concluding "inherent limitation" on a stuck score: enumerate the
57
62
  rendering inputs you have NOT verified — are the declared font faces
58
63
  actually resolved (the kit's cached faces and weights vs what the CSS
59
64
  asks for), computed styles vs authored styles, and
60
- platform text antialiasing and check them first. Run 13's "inherent
65
+ an `(lcd-text-enabled)` experiment mark in the environment stamp and
66
+ check them first (platform AA itself is pinned greyscale in every
67
+ canonical mount). Run 13's "inherent
61
68
  fidelity gap" was a missing font weight one check away; a stop rule is
62
69
  for avoiding thrash, never for converting an unverified hypothesis into
63
70
  a final answer.
@@ -363,16 +363,29 @@ function serverStale() {
363
363
  return false;
364
364
  }
365
365
  }
366
+ var STDERR_SECTION = "--- CLI stderr (remediation) ---";
367
+ function resultBody(result) {
368
+ const out = result.stdout.trim() === "" ? "" : result.stdout;
369
+ const err = result.stderr.trim() === "" ? "" : result.stderr;
370
+ if (result.ok || out === "" || err === "") return out === "" ? err : out;
371
+ return `${out.replace(/\n+$/, "")}
372
+ ${STDERR_SECTION}
373
+ ${err}`;
374
+ }
366
375
  function toolResult(result) {
367
376
  const stale = serverStale() ? `
368
377
  {"serverStale":true,"remediation":"packages/mcp changed since this server started \u2014 reload the Tendril MCP server before trusting this result or reporting a bug against it"}` : "";
369
- const body = (result.stdout.trim() !== "" ? result.stdout : result.stderr) + stale;
378
+ const body = resultBody(result) + stale;
370
379
  if (result.exitCode === 5) {
371
380
  return { content: [{ type: "text", text: `${body}
372
381
  {"exitCode":5,"note":"HONEST below-target result \u2014 the report and bundle are valid; this is not a tool failure. Either sub-bar scores, or (at --bar cert) a config demoted by absent-ink clusters despite at-bar numbers \u2014 the report names which."}` }] };
382
+ }
383
+ if (result.exitCode === 4) {
384
+ return { content: [{ type: "text", text: `${body}
385
+ {"exitCode":4,"note":"HONEST outcome \u2014 the CLI completed its work and stopped at a decision reserved for a HUMAN; nothing failed and re-running changes nothing. Show the output above plus the remediation to the user, get their answer, then proceed exactly as the remediation says \u2014 never confirm on their behalf."}` }] };
373
386
  }
374
387
  const text = result.ok ? body : `${body}
375
- {"exitCode":${result.exitCode},"note":"CLI exit-code contract: 3 input, 4 confirmation required, 6 fonts unproven, 7 recording incomplete"}`;
388
+ {"exitCode":${result.exitCode},"note":"CLI exit-code contract: 3 input, 6 fonts unproven, 7 recording incomplete"}`;
376
389
  return { content: [{ type: "text", text }], ...result.ok ? {} : { isError: true } };
377
390
  }
378
391
  var STATUS_PROMPT = {