@tendrilapp/cli 0.1.26 → 0.1.27

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.
Files changed (2) hide show
  1. package/dist/tendril.js +28 -2
  2. package/package.json +1 -1
package/dist/tendril.js CHANGED
@@ -1028,6 +1028,14 @@ function planSet(setDir, component, symbols, opts = {}) {
1028
1028
  const { manifest: existing, raw } = readManifestFile(setDir);
1029
1029
  const wantsNewDefaults = opts.defaults !== void 0 && JSON.stringify(opts.defaults) !== JSON.stringify(existing.defaults ?? {});
1030
1030
  const anythingRecorded = existing.reps.some((r) => RECORD_TOOLS.some((t) => existsSync(path.join(setDir, r.slug, `${t}.json`))));
1031
+ const scopeUpgraded = opts.variantScope === "component-set" && existing.variantScope !== "component-set";
1032
+ if (opts.variantScope === "component-set") {
1033
+ raw["variantScope"] = "component-set";
1034
+ const variants = symbols.map((s) => s.name).filter((n) => n.includes("="));
1035
+ if (variants.length > 0 && (scopeUpgraded || variants.length > (existing.latticeNames ?? []).length)) {
1036
+ raw["latticeNames"] = variants;
1037
+ }
1038
+ }
1031
1039
  if (!wantsNewDefaults || anythingRecorded) {
1032
1040
  if (opts.sample !== true) {
1033
1041
  const known = new Set(existing.reps.map((r) => r.nodeId));
@@ -1049,6 +1057,10 @@ function planSet(setDir, component, symbols, opts = {}) {
1049
1057
  return { manifest: manifest2, plan: { reps: [], notRecorded: [] }, resumed: true, toppedUp: appended.map((a) => ({ slug: a.slug, nodeId: a.nodeId })) };
1050
1058
  }
1051
1059
  }
1060
+ if (scopeUpgraded) {
1061
+ const manifest2 = writeManifest(setDir, raw);
1062
+ return { manifest: manifest2, plan: { reps: [], notRecorded: [] }, resumed: true };
1063
+ }
1052
1064
  return { manifest: existing, plan: { reps: [], notRecorded: [] }, resumed: true };
1053
1065
  }
1054
1066
  }
@@ -1921,7 +1933,7 @@ function tendrilCommand(args) {
1921
1933
  return `${tendrilInvocation()} ${args}`;
1922
1934
  }
1923
1935
  function quoteArg(value) {
1924
- if (/["`$\\]/.test(value)) throw new Error(`refusing to emit a shell argument containing quote/backslash/backtick/$: ${value}`);
1936
+ if (/["`$]/.test(value)) throw new Error(`refusing to emit a shell argument containing a double quote, backtick or $: ${value}`);
1925
1937
  return /[\s'*?[\]()&;|<>#~]/.test(value) ? `"${value}"` : value;
1926
1938
  }
1927
1939
  var NPX_INVOCATION, cached;
@@ -4314,6 +4326,19 @@ async function runSteps(page, spec, renderPose) {
4314
4326
  // a data-* or a name= is a place to park a string, which is
4315
4327
  // the dodge this check exists to catch.
4316
4328
  const PERCEIVED = ['placeholder', 'alt', 'aria-label', 'title', 'value'];
4329
+ // 'value' counts ONLY where the platform RENDERS it as the
4330
+ // control's text: text-entry inputs, textarea, and the
4331
+ // input-typed buttons whose value IS their label. Nearly
4332
+ // every element carries a value PROPERTY \u2014 a visible
4333
+ // checkbox or <button> with value={sentinel} would satisfy
4334
+ // an unrestricted check while no user ever perceives the
4335
+ // string, which reopens the exact dodge this check exists
4336
+ // to close (post-release review, 2026-08-14). password is
4337
+ // excluded because its value renders as dots, not as the
4338
+ // string. Fail-closed allowlist, not a blocklist.
4339
+ const VALUE_RENDERS = ['text', 'search', 'email', 'url', 'tel', 'number', 'submit', 'reset', 'button'];
4340
+ const valueRenders = (el) =>
4341
+ el instanceof HTMLTextAreaElement || (el instanceof HTMLInputElement && VALUE_RENDERS.includes(el.type));
4317
4342
  const visible = (el) => {
4318
4343
  const r = el.getBoundingClientRect();
4319
4344
  const cs = getComputedStyle(el);
@@ -4326,7 +4351,8 @@ async function runSteps(page, spec, renderPose) {
4326
4351
  PERCEIVED.some((a) => {
4327
4352
  // The LIVE value for form controls: React's defaultValue
4328
4353
  // sets the property, and the attribute may never appear.
4329
- const v = a === 'value' && 'value' in el ? el.value : el.getAttribute(a);
4354
+ if (a === 'value' && !valueRenders(el)) return false;
4355
+ const v = a === 'value' ? el.value : el.getAttribute(a);
4330
4356
  return typeof v === 'string' && v.includes(needle);
4331
4357
  }),
4332
4358
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tendrilapp/cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "Figma design systems → verified React components. CLI ruler + MCP server.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",