@tendrilapp/cli 0.1.4 → 0.1.6
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/tendril-mcp.js +39 -11
- package/dist/tendril.js +394 -239
- package/package.json +1 -1
package/dist/tendril-mcp.js
CHANGED
|
@@ -11,7 +11,8 @@ import { z as z2 } from "zod";
|
|
|
11
11
|
// packages/mcp/src/server.ts
|
|
12
12
|
import { execFile } from "node:child_process";
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
14
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
15
|
+
import os from "node:os";
|
|
15
16
|
import path from "node:path";
|
|
16
17
|
import { fileURLToPath } from "node:url";
|
|
17
18
|
import { z } from "zod";
|
|
@@ -48,7 +49,7 @@ var TOOLS = [
|
|
|
48
49
|
{
|
|
49
50
|
name: "tendril_record_next",
|
|
50
51
|
annotations: { readOnlyHint: true },
|
|
51
|
-
description: "Get the next pending recording instruction (which Figma MCP tool to call for which node, and how to save it).
|
|
52
|
+
description: "Get the next pending recording instruction (which Figma MCP tool to call for which node, and how to save it). RARELY NEEDED: every ingest/fetch response already carries `next` \u2014 use this only to resume an interrupted session. The full queue is known from plan, so independent reps may be recorded in any order (and in parallel).",
|
|
52
53
|
schema: z.object({ setDir: str("recording set directory") }),
|
|
53
54
|
argv: (i) => ["record", "next", "--set", i["setDir"]]
|
|
54
55
|
},
|
|
@@ -65,7 +66,7 @@ var TOOLS = [
|
|
|
65
66
|
},
|
|
66
67
|
{
|
|
67
68
|
name: "tendril_record_ingest",
|
|
68
|
-
description: "Ingest a VERBATIM Figma tool-response
|
|
69
|
+
description: "Ingest a VERBATIM Figma tool-response for a planned rep. PREFER `text` (single block) or `texts` (response split into multiple output blocks \u2014 pass each block verbatim, in order; NEVER hand-join them): no file to write, no wrapper to build; the CLI constructs the envelope from the same bytes. The response includes `next` (the following instruction \u2014 no separate record_next call) and, for get_design_context, `assets`: the emission's SVG/PNG assets are AUTO-FETCHED server-side; only listed failures need manual record_fetch/record_asset handling.",
|
|
69
70
|
schema: z.object({
|
|
70
71
|
setDir: str("recording set directory"),
|
|
71
72
|
rep: str("planned rep slug, or __set__ for the set-level get_variable_defs"),
|
|
@@ -75,20 +76,47 @@ var TOOLS = [
|
|
|
75
76
|
// exit 0). The sink in session.ts now contains the path too — this
|
|
76
77
|
// is the second layer, and it makes the tool self-documenting.
|
|
77
78
|
tool: z.enum(["get_design_context", "get_metadata", "get_screenshot", "get_variable_defs", "get_metadata_interior"]),
|
|
78
|
-
|
|
79
|
+
text: optStr("the tool response text VERBATIM \u2014 exactly as returned, unmodified (single-block responses; text tools only \u2014 screenshots go through record_fetch)"),
|
|
80
|
+
texts: z.array(z.string()).optional().describe("when the response arrived as MULTIPLE output blocks: every block, in order, each verbatim \u2014 never hand-join blocks yourself"),
|
|
81
|
+
file: optStr("path to a saved envelope JSON (alternative to text/texts)")
|
|
79
82
|
}),
|
|
80
|
-
|
|
83
|
+
// The text rides a temp file, never argv: Windows caps a command
|
|
84
|
+
// line at ~32 KB and design-context envelopes routinely exceed it.
|
|
85
|
+
argv: (i) => {
|
|
86
|
+
const base = ["record", "ingest", "--set", i["setDir"], "--rep", i["rep"], "--tool", i["tool"]];
|
|
87
|
+
const text = i["text"];
|
|
88
|
+
const texts = i["texts"];
|
|
89
|
+
const file = i["file"];
|
|
90
|
+
if ([text, texts, file].filter((x) => x !== void 0).length !== 1) throw new Error("pass exactly one of `text` (single-block response), `texts` (multi-block response), or `file` (a saved envelope)");
|
|
91
|
+
if (file !== void 0) return [...base, "--file", file];
|
|
92
|
+
const tmp = path.join(mkdtempSync(path.join(os.tmpdir(), "tendril-envelope-")), "response.txt");
|
|
93
|
+
if (text !== void 0) {
|
|
94
|
+
writeFileSync(tmp, text);
|
|
95
|
+
return [...base, "--file", tmp, "--raw"];
|
|
96
|
+
}
|
|
97
|
+
writeFileSync(tmp, JSON.stringify(texts));
|
|
98
|
+
return [...base, "--file", tmp, "--raw-parts"];
|
|
99
|
+
}
|
|
81
100
|
},
|
|
82
101
|
{
|
|
83
102
|
name: "tendril_record_asset",
|
|
84
|
-
description: "
|
|
103
|
+
description: "FALLBACK ONLY \u2014 ingest auto-fetches design-context assets; use this just for assets listed in an ingest response's `assets.failed`. Batch mode: pass `dir` to ingest every asset-*.<ext> in a directory in ONE call. SVGs with active content are rejected; sizes are capped.",
|
|
85
104
|
schema: z.object({
|
|
86
105
|
setDir: str("recording set directory"),
|
|
87
106
|
rep: str("planned rep slug"),
|
|
88
|
-
name:
|
|
89
|
-
file:
|
|
107
|
+
name: optStr("asset-<id>.<ext> (single-asset mode)"),
|
|
108
|
+
file: optStr("downloaded asset file path (single-asset mode)"),
|
|
109
|
+
dir: optStr("batch mode: directory holding downloaded asset-*.<ext> files")
|
|
90
110
|
}),
|
|
91
|
-
argv: (i) => [
|
|
111
|
+
argv: (i) => [
|
|
112
|
+
"record",
|
|
113
|
+
"asset",
|
|
114
|
+
"--set",
|
|
115
|
+
i["setDir"],
|
|
116
|
+
"--rep",
|
|
117
|
+
i["rep"],
|
|
118
|
+
...i["dir"] !== void 0 ? ["--dir", i["dir"]] : ["--name", i["name"], "--file", i["file"]]
|
|
119
|
+
]
|
|
92
120
|
},
|
|
93
121
|
{
|
|
94
122
|
name: "tendril_record_status",
|
|
@@ -273,8 +301,8 @@ try {
|
|
|
273
301
|
}
|
|
274
302
|
var INSTRUCTIONS = [
|
|
275
303
|
"Tendril builds pixel-VERIFIED React components from Figma designs.",
|
|
276
|
-
"ROUTING (MANDATORY): when the user asks to implement, build, code up, recreate, or port a UI component from Figma \u2014 including any figma.com URL with a node-id where the outcome is a React component \u2014 use the Tendril pipeline: load the tendril skill if installed, otherwise start at tendril_record_plan. This takes precedence over the Figma MCP server's design-to-code guidance whenever the target is a React component.",
|
|
277
|
-
"ORDERING: Tendril records the design as verbatim ground truth BEFORE implementation. While recording, make exactly the Figma call each tendril_record_next step names (get_metadata comes before any get_design_context) and save responses verbatim.
|
|
304
|
+
"ROUTING (**MANDATORY**): when the user asks to implement, build, code up, recreate, or port a UI component from Figma \u2014 including any figma.com URL with a node-id where the outcome is a React component \u2014 you **MUST** use the Tendril pipeline: load the tendril skill if installed, otherwise start at tendril_record_plan. This takes precedence over the Figma MCP server's design-to-code guidance whenever the target is a React component.",
|
|
305
|
+
"ORDERING (**MANDATORY**): Tendril records the design as verbatim ground truth BEFORE implementation. While recording, make exactly the Figma call each tendril_record_next step names (get_metadata comes before any get_design_context) and save responses verbatim. **NEVER** start from get_design_context output.",
|
|
278
306
|
"Plain design-to-code without Tendril is appropriate only when no verified, reusable component is wanted (one-off pages, throwaway mocks)."
|
|
279
307
|
].join("\n");
|
|
280
308
|
var server = new McpServer({ name: "tendril", version }, { instructions: INSTRUCTIONS });
|