@figtools/cli 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dryor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.es.md ADDED
@@ -0,0 +1,89 @@
1
+ *[Read in English](./README.md)*
2
+
3
+ # @figtools/cli
4
+
5
+ `@figtools/cli` resuelve una o varias URLs de Figma desde la línea de comandos y escribe el resultado en JSON o en un árbol de Markdown navegable, pensado para que un LLM lo use como fuente de datos sin tener que procesar un único JSON gigante. Usa [`@figtools/core`](../core) por debajo, así que no depende de la REST API oficial de Figma ni de una cuenta con Dev Mode.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g @figtools/cli
11
+ npx playwright install chromium
12
+ ```
13
+
14
+ También puedes instalarlo como dependencia local del proyecto y correrlo con `npx figtools`.
15
+
16
+ ## Examples
17
+
18
+ ### Iniciar sesión una sola vez
19
+
20
+ ```bash
21
+ figtools login
22
+ ```
23
+
24
+ Abre una ventana de Chromium en el login de Figma. Completa el login manualmente (soporta el flujo de Google SSO); al terminar, la sesión queda guardada en `~/.figma-scraper/session.json` y los comandos siguientes la reutilizan sin volver a pedir login.
25
+
26
+ ### Resolver una URL a JSON (por defecto, a stdout)
27
+
28
+ ```bash
29
+ figtools "https://www.figma.com/design/ABC123/Mi-Diseno?node-id=1-23"
30
+ ```
31
+
32
+ Si la URL no trae `node-id`, se resuelve la página activa completa del archivo.
33
+
34
+ ### Guardar el resultado en un archivo o carpeta
35
+
36
+ ```bash
37
+ # Un archivo .json puntual
38
+ figtools "https://www.figma.com/design/ABC123/Mi-Diseno?node-id=1-23" --output resultado.json
39
+
40
+ # Una carpeta: escribe "<carpeta>/<fileKey>-<nodeId>.json"
41
+ figtools "https://www.figma.com/design/ABC123/Mi-Diseno?node-id=1-23" --output ./salida
42
+ ```
43
+
44
+ ### Exportar como árbol de Markdown navegable
45
+
46
+ ```bash
47
+ figtools "https://www.figma.com/design/ABC123/Mi-Diseno" --format markdown --output ./docs
48
+ ```
49
+
50
+ Genera `./docs/<fileKey>/index.md` con un link por cada nodo de nivel superior, y una carpeta por cada nodo que tiene hijos (con su propio `index.md`), hasta llegar a los nodos hoja como `<slug>.md`. Este formato está pensado para que un LLM navegue el árbol de a un archivo por vez, en vez de recibir el diseño completo en un solo bloque de JSON.
51
+
52
+ ### Resolver varias URLs en una sola corrida
53
+
54
+ ```bash
55
+ figtools "https://www.figma.com/design/ABC123/A?node-id=1-1" "https://www.figma.com/design/ABC123/A?node-id=1-2" --format markdown --output ./docs
56
+ ```
57
+
58
+ Cada URL se resuelve en paralelo tras autenticar una sola vez. Si alguna falla, el proceso sigue con las demás y termina con código de salida `1`; los errores se listan en stderr al final.
59
+
60
+ ### Silenciar el progreso
61
+
62
+ ```bash
63
+ figtools "https://www.figma.com/design/ABC123/Mi-Diseno" --quiet
64
+ ```
65
+
66
+ Omite el mensaje `Resolving N URL(s)...` en stderr — útil si estás capturando stdout en un script.
67
+
68
+ ## Flags
69
+
70
+ | Flag | Valores | Default | Descripción |
71
+ | --- | --- | --- | --- |
72
+ | `--format` | `json`, `markdown` | `json` | Formato de salida |
73
+ | `--output` | ruta de archivo o carpeta | stdout (json) / `.` (markdown) | Dónde escribir el resultado |
74
+ | `--quiet` | — | `false` | Omite el mensaje de progreso en stderr |
75
+
76
+ ## Troubleshooting
77
+
78
+ - **`Error: unsupported extension "<ext>"`**: con `--format markdown`, `--output` siempre se trata como una carpeta. Con `--format json`, solo se acepta una ruta terminada en `.json` o sin extensión (tratada como carpeta); cualquier otra extensión falla explícitamente.
79
+ - **El proceso termina con código `1` pero imprimió resultados**: significa que al menos una de las URLs falló — revisa el bloque `URLs with errors:` al final de stderr para ver el código (`FigmaScraperErrorCode`) y mensaje de cada una. Ver la tabla de errores en el [README de `@figtools/core`](../core/README.es.md#errores-posibles).
80
+ - **`figtools login` no avanza**: la ventana de Chromium espera indefinidamente a que termines el login manual; confirma que llegaste a `https://www.figma.com/files/...` antes de cerrar la ventana.
81
+
82
+ ## Additional resources
83
+
84
+ - [`@figtools/core`](../core) — la librería subyacente, útil si prefieres integrar la resolución de URLs directamente en tu propio código en vez de invocar un binario.
85
+ - [README del monorepo](../../README.md) — visión general de `figtools`, ADRs y specs de aceptación.
86
+
87
+ ## License
88
+
89
+ MIT — ver [LICENSE](./LICENSE).
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ *[Leer en español](./README.es.md)*
2
+
3
+ # @figtools/cli
4
+
5
+ `@figtools/cli` resolves one or more Figma URLs from the command line and writes the result as JSON or as a navigable Markdown tree, meant to be used by an LLM as a data source without having to parse one giant JSON blob. It uses [`@figtools/core`](../core) under the hood, so it doesn't depend on the official Figma REST API or a Dev Mode account.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g @figtools/cli
11
+ npx playwright install chromium
12
+ ```
13
+
14
+ You can also install it as a local project dependency and run it with `npx figtools`.
15
+
16
+ ## Examples
17
+
18
+ ### Log in once
19
+
20
+ ```bash
21
+ figtools login
22
+ ```
23
+
24
+ Opens a Chromium window at the Figma login page. Complete the login manually (the Google SSO flow is supported); once done, the session is saved to `~/.figma-scraper/session.json` and later commands reuse it without asking to log in again.
25
+
26
+ ### Resolve a URL to JSON (stdout by default)
27
+
28
+ ```bash
29
+ figtools "https://www.figma.com/design/ABC123/My-Design?node-id=1-23"
30
+ ```
31
+
32
+ If the URL doesn't include `node-id`, the entire active page of the file is resolved.
33
+
34
+ ### Save the result to a file or folder
35
+
36
+ ```bash
37
+ # A single .json file
38
+ figtools "https://www.figma.com/design/ABC123/My-Design?node-id=1-23" --output result.json
39
+
40
+ # A folder: writes "<folder>/<fileKey>-<nodeId>.json"
41
+ figtools "https://www.figma.com/design/ABC123/My-Design?node-id=1-23" --output ./output
42
+ ```
43
+
44
+ ### Export as a navigable Markdown tree
45
+
46
+ ```bash
47
+ figtools "https://www.figma.com/design/ABC123/My-Design" --format markdown --output ./docs
48
+ ```
49
+
50
+ Generates `./docs/<fileKey>/index.md` with a link for each top-level node, and a folder per node that has children (with its own `index.md`), down to leaf nodes as `<slug>.md`. This format is meant for an LLM to navigate the tree one file at a time, instead of receiving the entire design in a single JSON blob.
51
+
52
+ ### Resolve several URLs in a single run
53
+
54
+ ```bash
55
+ figtools "https://www.figma.com/design/ABC123/A?node-id=1-1" "https://www.figma.com/design/ABC123/A?node-id=1-2" --format markdown --output ./docs
56
+ ```
57
+
58
+ Each URL is resolved in parallel after authenticating once. If any of them fail, the process continues with the rest and exits with code `1`; the errors are listed on stderr at the end.
59
+
60
+ ### Silence progress output
61
+
62
+ ```bash
63
+ figtools "https://www.figma.com/design/ABC123/My-Design" --quiet
64
+ ```
65
+
66
+ Omits the `Resolving N URL(s)...` message on stderr — useful if you're capturing stdout in a script.
67
+
68
+ ## Flags
69
+
70
+ | Flag | Values | Default | Description |
71
+ | --- | --- | --- | --- |
72
+ | `--format` | `json`, `markdown` | `json` | Output format |
73
+ | `--output` | file or folder path | stdout (json) / `.` (markdown) | Where to write the result |
74
+ | `--quiet` | — | `false` | Omits the progress message on stderr |
75
+
76
+ ## Troubleshooting
77
+
78
+ - **`Error: unsupported extension "<ext>"`**: with `--format markdown`, `--output` is always treated as a folder. With `--format json`, only a path ending in `.json` or with no extension (treated as a folder) is accepted; any other extension fails explicitly.
79
+ - **The process exits with code `1` but printed results**: at least one of the URLs failed — check the `URLs with errors:` block at the end of stderr for each one's code (`FigmaScraperErrorCode`) and message. See the error table in the [`@figtools/core` README](../core#possible-errors).
80
+ - **`figtools login` doesn't progress**: the Chromium window waits indefinitely for you to finish the manual login; confirm you reached `https://www.figma.com/files/...` before closing the window.
81
+
82
+ ## Additional resources
83
+
84
+ - [`@figtools/core`](../core) — the underlying library, useful if you'd rather integrate URL resolution directly into your own code instead of invoking a binary.
85
+ - [Monorepo README](../../README.md) — overview of `figtools`, ADRs, and acceptance specs.
86
+
87
+ ## License
88
+
89
+ MIT — see [LICENSE](./LICENSE).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ export type OutputFormat = "json" | "markdown";
2
+ export interface ParsedArgs {
3
+ urls: string[];
4
+ format: OutputFormat;
5
+ outputPath?: string;
6
+ quiet: boolean;
7
+ command?: "login";
8
+ }
9
+ type ParseArgsError = {
10
+ code: "VALIDATION_NO_URLS" | "VALIDATION_UNSUPPORTED_EXTENSION";
11
+ message: string;
12
+ };
13
+ export type ParseArgsResult = {
14
+ ok: true;
15
+ value: ParsedArgs;
16
+ } | {
17
+ ok: false;
18
+ error: ParseArgsError;
19
+ };
20
+ export type OutputTarget = {
21
+ kind: "stdout";
22
+ } | {
23
+ kind: "file";
24
+ path: string;
25
+ } | {
26
+ kind: "directory";
27
+ path: string;
28
+ } | {
29
+ kind: "unsupported-extension";
30
+ extension: string;
31
+ };
32
+ export declare function parseArgs(argv: string[]): ParseArgsResult;
33
+ export declare function decideOutputTarget(outputPath: string | undefined, format: OutputFormat): OutputTarget;
34
+ export {};
35
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,oBAAoB,GAAG,kCAAkC,CAAC;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC;AAEzC,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA+BzD;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,YAAY,GACnB,YAAY,CAad"}
package/dist/cli.js ADDED
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ import { extname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { CookieSessionStore, PlaywrightFigmaGateway, PlaywrightLogin, createFigmaScraperCore } from "@figtools/core";
5
+ import { mkdir, writeFile } from "node:fs/promises";
6
+ async function resolveAll(core, urls) {
7
+ const sessionResult = await core.ensureSession();
8
+ if (!sessionResult.ok) return urls.map((url)=>({
9
+ url,
10
+ result: sessionResult
11
+ }));
12
+ const settled = await Promise.allSettled(urls.map((url)=>core.resolveUrl(url)));
13
+ return urls.map((url, i)=>{
14
+ const outcome = settled[i];
15
+ if ("fulfilled" === outcome.status) return {
16
+ url,
17
+ result: outcome.value
18
+ };
19
+ return {
20
+ url,
21
+ result: {
22
+ ok: false,
23
+ error: {
24
+ code: "AUTHENTICATION_FAILED",
25
+ message: "Unexpected error while resolving the URL"
26
+ }
27
+ }
28
+ };
29
+ });
30
+ }
31
+ async function writeAsJson(fileKey, result, options) {
32
+ const json = JSON.stringify(result, null, 2);
33
+ if (!options.outputPath) return void console.log(json);
34
+ const { outputPath } = options;
35
+ if (".json" === extname(outputPath)) return void await writeFile(outputPath, json, "utf8");
36
+ await mkdir(outputPath, {
37
+ recursive: true
38
+ });
39
+ const nodeId = result.id.replace(/:/g, "-");
40
+ await writeFile(join(outputPath, `${fileKey}-${nodeId}.json`), json, "utf8");
41
+ }
42
+ function slugify(name) {
43
+ return name.replace(/\s*\([^)]*\)/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
44
+ }
45
+ function slugifyWithCollisions(names) {
46
+ const slugs = names.map(slugify);
47
+ const count = new Map();
48
+ return slugs.map((slug)=>{
49
+ const n = (count.get(slug) ?? 0) + 1;
50
+ count.set(slug, n);
51
+ return 1 === n ? slug : `${slug}-[${n}]`;
52
+ });
53
+ }
54
+ function isFigmaPage(result) {
55
+ return "nodes" in result;
56
+ }
57
+ function leafContent(node) {
58
+ return `# ${node.name}\n\ntype: ${node.type}\n`;
59
+ }
60
+ function containerContent(name, children, childSlugs) {
61
+ const links = children.map((child, i)=>{
62
+ const slug = childSlugs[i];
63
+ const href = 0 === child.children.length ? `${slug}.md` : `${slug}/index.md`;
64
+ return `- [${child.name}](${href})`;
65
+ }).join("\n");
66
+ return `# ${name}\n\n${links}\n`;
67
+ }
68
+ async function writeChildNode(node, parentDir, slug) {
69
+ if (0 === node.children.length) return void await writeFile(join(parentDir, `${slug}.md`), leafContent(node), "utf8");
70
+ const nodeDir = join(parentDir, slug);
71
+ await mkdir(nodeDir, {
72
+ recursive: true
73
+ });
74
+ const childSlugs = slugifyWithCollisions(node.children.map((c)=>c.name));
75
+ await writeFile(join(nodeDir, "index.md"), containerContent(node.name, node.children, childSlugs), "utf8");
76
+ for(let i = 0; i < node.children.length; i++)await writeChildNode(node.children[i], nodeDir, childSlugs[i]);
77
+ }
78
+ async function writeAsMarkdownTree(fileKey, result, options) {
79
+ const fileKeyDir = join(options.outputDir, fileKey);
80
+ await mkdir(fileKeyDir, {
81
+ recursive: true
82
+ });
83
+ if (isFigmaPage(result)) {
84
+ const childSlugs = slugifyWithCollisions(result.nodes.map((n)=>n.name));
85
+ await writeFile(join(fileKeyDir, "index.md"), containerContent(result.name, result.nodes, childSlugs), "utf8");
86
+ for(let i = 0; i < result.nodes.length; i++)await writeChildNode(result.nodes[i], fileKeyDir, childSlugs[i]);
87
+ return;
88
+ }
89
+ if (0 === result.children.length) {
90
+ const [slug] = slugifyWithCollisions([
91
+ result.name
92
+ ]);
93
+ await writeFile(join(fileKeyDir, `${slug}.md`), leafContent(result), "utf8");
94
+ return;
95
+ }
96
+ const childSlugs = slugifyWithCollisions(result.children.map((c)=>c.name));
97
+ await writeFile(join(fileKeyDir, "index.md"), containerContent(result.name, result.children, childSlugs), "utf8");
98
+ for(let i = 0; i < result.children.length; i++)await writeChildNode(result.children[i], fileKeyDir, childSlugs[i]);
99
+ }
100
+ function parseArgs(argv) {
101
+ if ("login" === argv[0]) return {
102
+ ok: true,
103
+ value: {
104
+ command: "login",
105
+ urls: [],
106
+ format: "json",
107
+ quiet: false
108
+ }
109
+ };
110
+ let format = "json";
111
+ let outputPath;
112
+ let quiet = false;
113
+ const urls = [];
114
+ for(let i = 0; i < argv.length; i++){
115
+ const arg = argv[i];
116
+ if ("--format" === arg) format = argv[++i];
117
+ else if ("--output" === arg) outputPath = argv[++i];
118
+ else if ("--quiet" === arg) quiet = true;
119
+ else if (!arg.startsWith("--")) urls.push(arg);
120
+ }
121
+ if (0 === urls.length) return {
122
+ ok: false,
123
+ error: {
124
+ code: "VALIDATION_NO_URLS",
125
+ message: "At least one Figma URL is required"
126
+ }
127
+ };
128
+ return {
129
+ ok: true,
130
+ value: {
131
+ urls,
132
+ format,
133
+ outputPath,
134
+ quiet
135
+ }
136
+ };
137
+ }
138
+ function decideOutputTarget(outputPath, format) {
139
+ if (!outputPath) return "markdown" === format ? {
140
+ kind: "directory",
141
+ path: "."
142
+ } : {
143
+ kind: "stdout"
144
+ };
145
+ if ("markdown" === format) return {
146
+ kind: "directory",
147
+ path: outputPath
148
+ };
149
+ const ext = extname(outputPath);
150
+ if (!ext) return {
151
+ kind: "directory",
152
+ path: outputPath
153
+ };
154
+ if (".json" === ext) return {
155
+ kind: "file",
156
+ path: outputPath
157
+ };
158
+ return {
159
+ kind: "unsupported-extension",
160
+ extension: ext
161
+ };
162
+ }
163
+ function extractFileKey(url) {
164
+ const match = url.match(/\/(?:file|design|proto|board)\/([^/?]+)/);
165
+ return match ? match[1] : url;
166
+ }
167
+ async function main(argv) {
168
+ const parsed = parseArgs(argv);
169
+ if (!parsed.ok) {
170
+ process.stderr.write(`Error: ${parsed.error.message}\n`);
171
+ process.exit(1);
172
+ }
173
+ const { command, urls, format, outputPath, quiet } = parsed.value;
174
+ const core = createFigmaScraperCore({
175
+ sessionStore: new CookieSessionStore(),
176
+ interactiveLogin: new PlaywrightLogin(),
177
+ gateway: new PlaywrightFigmaGateway()
178
+ });
179
+ if ("login" === command) {
180
+ const result = await core.reauthenticate();
181
+ if (!result.ok) {
182
+ process.stderr.write(`Authentication error: ${result.error.message}\n`);
183
+ process.exit(1);
184
+ }
185
+ process.stderr.write("Logged in successfully.\n");
186
+ process.exit(0);
187
+ }
188
+ const outputTarget = decideOutputTarget(outputPath, format);
189
+ if ("unsupported-extension" === outputTarget.kind) {
190
+ process.stderr.write(`Error: unsupported extension "${outputTarget.extension}"\n`);
191
+ process.exit(1);
192
+ }
193
+ if (!quiet) process.stderr.write(`Resolving ${urls.length} URL(s)...\n`);
194
+ const resolutions = await resolveAll(core, urls);
195
+ let hadFailure = false;
196
+ for (const { url, result } of resolutions){
197
+ if (!result.ok) {
198
+ hadFailure = true;
199
+ continue;
200
+ }
201
+ const fileKey = extractFileKey(url);
202
+ if ("markdown" === format) {
203
+ const dir = "directory" === outputTarget.kind ? outputTarget.path : ".";
204
+ await writeAsMarkdownTree(fileKey, result.value, {
205
+ outputDir: dir
206
+ });
207
+ } else {
208
+ const writePath = "file" === outputTarget.kind ? outputTarget.path : "directory" === outputTarget.kind ? outputTarget.path : void 0;
209
+ await writeAsJson(fileKey, result.value, {
210
+ outputPath: writePath
211
+ });
212
+ }
213
+ }
214
+ const failures = resolutions.filter((r)=>!r.result.ok);
215
+ if (failures.length > 0) {
216
+ process.stderr.write("\nURLs with errors:\n");
217
+ for (const { url, result } of failures)if (!result.ok) process.stderr.write(` ${url}: ${result.error.code} — ${result.error.message}\n`);
218
+ }
219
+ process.exit(hadFailure ? 1 : 0);
220
+ }
221
+ if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(2));
222
+ export { decideOutputTarget, parseArgs };
@@ -0,0 +1,6 @@
1
+ import type { FigmaScrapeResult } from "@figtools/core";
2
+ export interface JsonWriterOptions {
3
+ outputPath?: string;
4
+ }
5
+ export declare function writeAsJson(fileKey: string, result: FigmaScrapeResult, options: JsonWriterOptions): Promise<void>;
6
+ //# sourceMappingURL=json-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-writer.d.ts","sourceRoot":"","sources":["../../src/output/json-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAkBf"}
@@ -0,0 +1,6 @@
1
+ import type { FigmaScrapeResult } from "@figtools/core";
2
+ export interface MarkdownWriterOptions {
3
+ outputDir: string;
4
+ }
5
+ export declare function writeAsMarkdownTree(fileKey: string, result: FigmaScrapeResult, options: MarkdownWriterOptions): Promise<void>;
6
+ //# sourceMappingURL=markdown-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-writer.d.ts","sourceRoot":"","sources":["../../src/output/markdown-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAoCD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -0,0 +1,2 @@
1
+ export declare function slugifyWithCollisions(names: string[]): string[];
2
+ //# sourceMappingURL=slugify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../src/output/slugify.ts"],"names":[],"mappings":"AAQA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQ/D"}
@@ -0,0 +1,7 @@
1
+ import type { FigmaScraperCore, FigmaScrapeResult, FigmaScraperError, Result } from "@figtools/core";
2
+ export interface UrlResolution {
3
+ url: string;
4
+ result: Result<FigmaScrapeResult, FigmaScraperError>;
5
+ }
6
+ export declare function resolveAll(core: FigmaScraperCore, urls: string[]): Promise<UrlResolution[]>;
7
+ //# sourceMappingURL=resolve-all.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-all.d.ts","sourceRoot":"","sources":["../src/resolve-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAErG,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;CACtD;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,aAAa,EAAE,CAAC,CAsB1B"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@figtools/cli",
3
+ "version": "0.1.0",
4
+ "description": "Resolves one or more Figma URLs from the command line and writes the result as JSON or as a navigable markdown tree meant to be used as a data source by an LLM.",
5
+ "keywords": [
6
+ "figma",
7
+ "figma-api",
8
+ "cli",
9
+ "scraper",
10
+ "markdown",
11
+ "llm"
12
+ ],
13
+ "homepage": "https://github.com/dryor/figtools#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/dryor/figtools.git",
17
+ "directory": "packages/cli"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/dryor/figtools/issues"
21
+ },
22
+ "license": "MIT",
23
+ "type": "module",
24
+ "engines": {
25
+ "node": ">=24"
26
+ },
27
+ "bin": {
28
+ "figtools": "./dist/cli.js"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "dependencies": {
37
+ "playwright": "^1.61.1",
38
+ "@figtools/core": "0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "@rslib/core": "^0.23.2",
42
+ "@types/node": "^26.1.1",
43
+ "typescript": "^7.0.2",
44
+ "vitest": "^4.1.10"
45
+ },
46
+ "scripts": {
47
+ "build": "rslib build",
48
+ "test": "vitest run --project unit",
49
+ "test:watch": "vitest --project unit",
50
+ "typecheck": "tsc --noEmit"
51
+ }
52
+ }