@tendrilapp/cli 0.1.20 → 0.1.23

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
@@ -39,16 +39,30 @@ user must reload the session to apply; say that out loud. Offer ONCE
39
39
  per session; never run it unoffered (it edits the user's settings),
40
40
  and if declined, proceed without mentioning it again.
41
41
 
42
- Long generator/score waits: silence is not a health signal. The scorer
43
- appends a timestamped line to `<candidateDir>/score-history.jsonl`
44
- after EVERY completed round the file APPEARS ONLY AFTER THE FIRST
45
- score, so its absence early in a run is normal, not frozen. Poll it
46
- (and the candidate dir's mtimes) about every 30 seconds and relay
47
- progress to the user ("round 3 scored: 15/21 at floor 0.885"). A
48
- healthy run can take 30+ minutes; never kill on elapsed time alone —
49
- count staleness from the LAST CHANGE to either signal, not from run
50
- start, and when past ~20 minutes of true staleness, ask the user
51
- before killing.
42
+ Long generator/score waits: silence is not a health signal, and POLL
43
+ cadence is not RELAY cadence (run 13: rounds landed 6–11 minutes apart,
44
+ so a correct 30s poll still left the user in 9-minute silences they
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
53
+ alone — count staleness from the LAST CHANGE to any signal, and when
54
+ past ~20 minutes of true staleness, ask the user before killing.
55
+
56
+ Before concluding "inherent limitation" on a stuck score: enumerate the
57
+ rendering inputs you have NOT verified — are the declared font faces
58
+ actually resolved (the kit's cached faces and weights vs what the CSS
59
+ asks for), computed styles vs authored styles, and
60
+ an `(lcd-text-enabled)` experiment mark in the environment stamp — and
61
+ check them first (platform AA itself is pinned greyscale in every
62
+ canonical mount). Run 13's "inherent
63
+ fidelity gap" was a missing font weight one check away; a stop rule is
64
+ for avoiding thrash, never for converting an unverified hypothesis into
65
+ a final answer.
52
66
 
53
67
  Batch runs (several components in one session):
54
68
  - Cap concurrent recorders at FOUR. All recorders share one Figma
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // packages/mcp/src/bin.ts
3
+ // ../../../../../../Users/jordyarnoldussen/Desktop/Tendril/packages/mcp/src/bin.ts
4
4
  import { readFileSync as readFileSync2 } from "node:fs";
5
5
  import path2 from "node:path";
6
6
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -8,7 +8,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
9
  import { z as z2 } from "zod";
10
10
 
11
- // packages/mcp/src/server.ts
11
+ // ../../../../../../Users/jordyarnoldussen/Desktop/Tendril/packages/mcp/src/server.ts
12
12
  import { execFile } from "node:child_process";
13
13
  import { createHash } from "node:crypto";
14
14
  import { existsSync, mkdtempSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
@@ -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 = {
@@ -410,7 +423,7 @@ var IMPLEMENT_PROMPT = {
410
423
  ].join("\n")
411
424
  };
412
425
 
413
- // packages/mcp/src/bin.ts
426
+ // ../../../../../../Users/jordyarnoldussen/Desktop/Tendril/packages/mcp/src/bin.ts
414
427
  var version = "dev";
415
428
  try {
416
429
  version = JSON.parse(readFileSync2(path2.join(path2.dirname(fileURLToPath2(import.meta.url)), "..", "package.json"), "utf8")).version ?? "dev";