@tendrilapp/cli 0.1.30 → 0.1.31

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
@@ -131,7 +131,12 @@ Batch runs (several components in one session):
131
131
  2. `tendril_record_plan` with the response text passed VERBATIM via
132
132
  `metadataParts` (one array entry per response block, in order —
133
133
  single-block responses may use `metadata` instead; nothing is saved
134
- to a file, and never hand-join blocks) the queue plan. The plan
134
+ to a file, and never hand-join blocks), AND `figmaFile` set to the
135
+ file key from the URL you were handed (figma.com/design/<KEY>/… —
136
+ pass exactly KEY): it is recorded as the set's file identity for
137
+ cross-bundle composition, captured at plan time only and never
138
+ backfillable — a set planned without it can never join across
139
+ bundles → the queue plan. The plan
135
140
  records the FULL variant matrix — every pose the set defines
136
141
  (sampling exists only as a human-run CLI flag; the MCP surface
137
142
  cannot sample). CROSS-CHECK `variantsFound` in the plan output
@@ -37,7 +37,8 @@ var TOOLS = [
37
37
  metadata: optStr("ONLY when get_metadata genuinely returned one single block: its text verbatim (otherwise use metadataParts)"),
38
38
  metadataFiles: z.array(z.string()).optional().describe('saved verbatim get_metadata envelope file paths \u2014 JSON shape {"content":[{"type":"text","text":"<frame \u2026>"}]}; optionally <file>@<frameId>. Prefer metadataParts: no file to write'),
39
39
  defaults: z.array(z.string()).optional().describe(`axis defaults as "Axis=Value" (from the user's defaultsToConfirm answers; may re-plan a set with nothing recorded yet)`),
40
- componentSet: optStr("record only this component set (the user's pick from a multiple-component-sets error)")
40
+ componentSet: optStr("record only this component set (the user's pick from a multiple-component-sets error)"),
41
+ figmaFile: optStr("the Figma file key from the design URL you were handed \u2014 figma.com/design/<KEY>/\u2026, pass exactly <KEY>. ALWAYS pass it on a fresh plan: it is the set's recorded file identity for cross-bundle composition (ADR-013), captured at plan time only and never backfillable later")
41
42
  }),
42
43
  // Texts ride temp files, never argv: Windows caps a command line at
43
44
  // ~32 KB and metadata envelopes can exceed it.
@@ -60,7 +61,11 @@ var TOOLS = [
60
61
  }
61
62
  }
62
63
  if (argvOut.length === 6) throw new Error("nothing to plan from \u2014 pass metadataParts (normal), metadata (single-block), or metadataFiles");
63
- argvOut.push(...i["defaults"] !== void 0 ? ["--default", ...i["defaults"]] : [], ...i["componentSet"] !== void 0 ? ["--component-set", i["componentSet"]] : []);
64
+ argvOut.push(
65
+ ...i["defaults"] !== void 0 ? ["--default", ...i["defaults"]] : [],
66
+ ...i["componentSet"] !== void 0 ? ["--component-set", i["componentSet"]] : [],
67
+ ...typeof i["figmaFile"] === "string" ? ["--figma-file", i["figmaFile"]] : []
68
+ );
64
69
  return argvOut;
65
70
  }
66
71
  },
package/dist/tendril.js CHANGED
@@ -1069,6 +1069,9 @@ function planSet(setDir, component, symbols, opts = {}) {
1069
1069
  const manifest = {
1070
1070
  version: 1,
1071
1071
  component,
1072
+ ...opts.figmaFile !== void 0 ? { figmaFile: opts.figmaFile } : {},
1073
+ ...opts.componentSetNode !== void 0 ? { componentSetNode: opts.componentSetNode } : {},
1074
+ ...opts.figmaComponentName !== void 0 ? { figmaComponentName: opts.figmaComponentName } : {},
1072
1075
  ...opts.sourceFrames !== void 0 ? { sourceFrames: opts.sourceFrames } : {},
1073
1076
  ...opts.variantScope !== void 0 ? { variantScope: opts.variantScope } : {},
1074
1077
  ...opts.defaults !== void 0 && Object.keys(opts.defaults).length > 0 ? { defaults: opts.defaults } : {},
@@ -1215,8 +1218,27 @@ var init_session = __esm({
1215
1218
  SessionManifestSchema = z4.object({
1216
1219
  version: z4.literal(1),
1217
1220
  component: z4.string().min(1),
1221
+ /** ADR-013 slice 1 (v4, capture-only): cross-set identity, captured
1222
+ * at RECORD TIME on fresh plans only — NEVER backfilled (the
1223
+ * recording-set hash covers this file's bytes, so adding fields to
1224
+ * a set that already produced bundles voids their provenance).
1225
+ * `figmaFile` is the Figma file key from the URL the agent host was
1226
+ * handed — an AGENT ASSERTION, disclosed, never enforcement-bearing
1227
+ * alone (ADR-013 §1). `componentSetNode`/`figmaComponentName` are
1228
+ * the enclosing set's id and Figma-side name as the planner's own
1229
+ * metadata parse saw them — the identity that stops a re-recording
1230
+ * of the same component from reading as a different one (`component`
1231
+ * below is the plan-time DISPLAY name; the measured case recorded
1232
+ * one control as "Lucent Control (Dark)" and "control"). All
1233
+ * optional: legacy sets simply cannot join, disclosed. */
1234
+ figmaFile: z4.string().regex(/^[A-Za-z0-9]{8,64}$/).optional(),
1235
+ componentSetNode: z4.string().optional(),
1236
+ figmaComponentName: z4.string().optional(),
1218
1237
  sourceFrames: z4.record(z4.string(), z4.string()).optional(),
1219
- reps: z4.array(z4.object({ slug: z4.string(), nodeId: z4.string(), sourceFrame: z4.string().optional() })).min(1),
1238
+ /** Per-rep `componentKey` is SEEDED (ADR-013 v4: the one-call MCP
1239
+ * probe decides whether anything can ever fill it) so interim sets
1240
+ * are not born cross-file-dead if keys turn out to be available. */
1241
+ reps: z4.array(z4.object({ slug: z4.string(), nodeId: z4.string(), sourceFrame: z4.string().optional(), componentKey: z4.string().optional() })).min(1),
1220
1242
  notRecorded: z4.string().optional(),
1221
1243
  /** Explicit axis-default overrides from `record plan --default`.
1222
1244
  * Recorded-truth-side on purpose: the default decides which pose an
@@ -7873,10 +7895,10 @@ function symbolsFromMetadataEnvelope(file, sourceFrame) {
7873
7895
  nodeId: node.id,
7874
7896
  name: node.name,
7875
7897
  ...sourceFrame !== void 0 ? { sourceFrame } : {},
7876
- ...ancestor !== void 0 ? { setName: ancestor } : {}
7898
+ ...ancestor !== void 0 ? { setName: ancestor.name, setNodeId: ancestor.id } : {}
7877
7899
  });
7878
7900
  }
7879
- const next = node.type !== "COMPONENT" && node.name !== "" ? node.name : ancestor;
7901
+ const next = node.type !== "COMPONENT" && node.name !== "" ? { name: node.name, id: node.id } : ancestor;
7880
7902
  for (const child of node.children) walk2(child, next);
7881
7903
  };
7882
7904
  const forest = parseMetadataForest(text);
@@ -8014,10 +8036,25 @@ function runRecordPlan(opts) {
8014
8036
  if (metadataTruncated) {
8015
8037
  warn(opts, "get_metadata response appears TRUNCATED (unclosed structure) \u2014 the variant list below may be incomplete. Cross-check variantsFound against the variant count Figma shows for this component set; re-fetch the metadata if lower.");
8016
8038
  }
8039
+ if (opts.figmaFile !== void 0 && !/^[A-Za-z0-9]{8,64}$/.test(opts.figmaFile)) {
8040
+ fail(opts, ExitCode.InputValidation, {
8041
+ error: `--figma-file must be the bare file key from the Figma URL (letters and digits only) \u2014 got ${JSON.stringify(opts.figmaFile)}`,
8042
+ code: "bad-figma-file",
8043
+ remediation: "From https://www.figma.com/design/<KEY>/<name>?node-id=\u2026, pass exactly <KEY>."
8044
+ });
8045
+ }
8046
+ const setIdentity = (() => {
8047
+ if (variantScope !== "component-set") return {};
8048
+ const ids = [...new Set(symbols.map((s) => s.setNodeId).filter((x) => x !== void 0))];
8049
+ const names = [...new Set(symbols.map((s) => s.setName).filter((x) => x !== void 0))];
8050
+ return ids.length === 1 && names.length === 1 ? { componentSetNode: ids[0], figmaComponentName: names[0] } : {};
8051
+ })();
8017
8052
  const { manifest, plan, resumed, toppedUp } = planSet(opts.setDir, opts.component, symbols, {
8018
8053
  ...Object.keys(defaults).length > 0 ? { defaults } : {},
8019
8054
  ...opts.sample === true ? { sample: true } : {},
8020
- variantScope
8055
+ variantScope,
8056
+ ...opts.figmaFile !== void 0 ? { figmaFile: opts.figmaFile } : {},
8057
+ ...setIdentity
8021
8058
  });
8022
8059
  const defaultReports = reportAxisDefaults(symbols, Object.keys(defaults).length > 0 ? defaults : void 0);
8023
8060
  const toConfirm = resumed ? [] : defaultReports.filter((r) => (r.rule === "non-interaction" || r.rule === "frequency") && r.domain.length > 1);
@@ -12230,7 +12267,8 @@ var init_server = __esm({
12230
12267
  metadata: optStr("ONLY when get_metadata genuinely returned one single block: its text verbatim (otherwise use metadataParts)"),
12231
12268
  metadataFiles: z12.array(z12.string()).optional().describe('saved verbatim get_metadata envelope file paths \u2014 JSON shape {"content":[{"type":"text","text":"<frame \u2026>"}]}; optionally <file>@<frameId>. Prefer metadataParts: no file to write'),
12232
12269
  defaults: z12.array(z12.string()).optional().describe(`axis defaults as "Axis=Value" (from the user's defaultsToConfirm answers; may re-plan a set with nothing recorded yet)`),
12233
- componentSet: optStr("record only this component set (the user's pick from a multiple-component-sets error)")
12270
+ componentSet: optStr("record only this component set (the user's pick from a multiple-component-sets error)"),
12271
+ figmaFile: optStr("the Figma file key from the design URL you were handed \u2014 figma.com/design/<KEY>/\u2026, pass exactly <KEY>. ALWAYS pass it on a fresh plan: it is the set's recorded file identity for cross-bundle composition (ADR-013), captured at plan time only and never backfillable later")
12234
12272
  }),
12235
12273
  // Texts ride temp files, never argv: Windows caps a command line at
12236
12274
  // ~32 KB and metadata envelopes can exceed it.
@@ -12253,7 +12291,11 @@ var init_server = __esm({
12253
12291
  }
12254
12292
  }
12255
12293
  if (argvOut.length === 6) throw new Error("nothing to plan from \u2014 pass metadataParts (normal), metadata (single-block), or metadataFiles");
12256
- argvOut.push(...i["defaults"] !== void 0 ? ["--default", ...i["defaults"]] : [], ...i["componentSet"] !== void 0 ? ["--component-set", i["componentSet"]] : []);
12294
+ argvOut.push(
12295
+ ...i["defaults"] !== void 0 ? ["--default", ...i["defaults"]] : [],
12296
+ ...i["componentSet"] !== void 0 ? ["--component-set", i["componentSet"]] : [],
12297
+ ...typeof i["figmaFile"] === "string" ? ["--figma-file", i["figmaFile"]] : []
12298
+ );
12257
12299
  return argvOut;
12258
12300
  }
12259
12301
  },
@@ -14073,7 +14115,7 @@ function buildProgram() {
14073
14115
  await runActivate2({ ...flags, serviceUrl: local["serviceUrl"] });
14074
14116
  });
14075
14117
  const record = program.command("record").description("Agent-driven recording protocol: plan the queue, get instructions, ingest verbatim envelopes, resume from disk.");
14076
- record.command("plan").requiredOption("--set <dir>", "recording set directory").requiredOption("--component <name>", "component/system name").option("--metadata <file...>", "verbatim get_metadata envelope(s), optionally <file>@<frameId>").option("--metadata-raw-file <file>", "the get_metadata response TEXT exactly as received (single block); the CLI builds the envelope itself").option("--metadata-raw-parts-file <file>", "a JSON array of the get_metadata response blocks, each verbatim, in order (multi-block responses)").option("--default <axis=value...>", "explicit axis default, e.g. --default State=Rest (repeatable; persisted to the manifest; may re-plan an unrecorded set)").option("--component-set <name>", "when the metadata holds several component sets, record only this one (name = the set label in the plan error)").option("--sample", "cost sampling: anchor + one-factor + conflict crosses only (the FULL variant matrix is the default; sampling is blind to multi-axis interactions)").action(async (_o, cmd) => {
14118
+ record.command("plan").requiredOption("--set <dir>", "recording set directory").requiredOption("--component <name>", "component/system name").option("--metadata <file...>", "verbatim get_metadata envelope(s), optionally <file>@<frameId>").option("--metadata-raw-file <file>", "the get_metadata response TEXT exactly as received (single block); the CLI builds the envelope itself").option("--metadata-raw-parts-file <file>", "a JSON array of the get_metadata response blocks, each verbatim, in order (multi-block responses)").option("--default <axis=value...>", "explicit axis default, e.g. --default State=Rest (repeatable; persisted to the manifest; may re-plan an unrecorded set)").option("--component-set <name>", "when the metadata holds several component sets, record only this one (name = the set label in the plan error)").option("--sample", "cost sampling: anchor + one-factor + conflict crosses only (the FULL variant matrix is the default; sampling is blind to multi-axis interactions)").option("--figma-file <key>", "the Figma file key from the design URL (figma.com/design/<KEY>/\u2026) \u2014 recorded as the set's file identity for cross-bundle composition (ADR-013; fresh plans only, never backfilled)").action(async (_o, cmd) => {
14077
14119
  const flags = globalFlags(cmd.parent.parent);
14078
14120
  const local = cmd.opts();
14079
14121
  const { runRecordPlan: runRecordPlan2 } = await Promise.resolve().then(() => (init_record(), record_exports));
@@ -14082,6 +14124,7 @@ function buildProgram() {
14082
14124
  setDir: local["set"],
14083
14125
  component: local["component"],
14084
14126
  sample: local["sample"],
14127
+ ...local["figmaFile"] !== void 0 ? { figmaFile: local["figmaFile"] } : {},
14085
14128
  ...local["metadata"] !== void 0 ? { metadataFiles: local["metadata"] } : {},
14086
14129
  ...local["metadataRawFile"] !== void 0 ? { metadataRawFile: local["metadataRawFile"] } : {},
14087
14130
  ...local["metadataRawPartsFile"] !== void 0 ? { metadataRawPartsFile: local["metadataRawPartsFile"] } : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tendrilapp/cli",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Figma design systems → verified React components. CLI ruler + MCP server.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",