framer-dalton 0.0.1 → 0.0.2

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/cli.js CHANGED
@@ -6,7 +6,7 @@ import path from 'path';
6
6
  import { spawn } from 'child_process';
7
7
  import { fileURLToPath } from 'url';
8
8
 
9
- /* @framer/ai CLI v0.0.1 */
9
+ /* @framer/ai CLI v0.0.2 */
10
10
  var __defProp = Object.defineProperty;
11
11
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
12
12
  function getConfigDir() {
@@ -73,7 +73,7 @@ function getConfigPath() {
73
73
  __name(getConfigPath, "getConfigPath");
74
74
  var __filename$1 = fileURLToPath(import.meta.url);
75
75
  var __dirname$1 = path.dirname(__filename$1);
76
- var VERSION = "0.0.1" ;
76
+ var VERSION = "0.0.2" ;
77
77
  var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19987;
78
78
  async function getRelayServerVersion(port = RELAY_PORT) {
79
79
  try {
@@ -160,6 +160,709 @@ async function ensureRelayServer(options = {}) {
160
160
  }
161
161
  __name(ensureRelayServer, "ensureRelayServer");
162
162
 
163
+ // src/skill-docs.ts
164
+ var skillDocs = `## CLI Usage
165
+
166
+ Use \`npx framer-dalton@latest\` for the first command in a session to ensure you're using the latest version. Subsequent commands can omit \`@latest\`:
167
+
168
+ \`\`\`bash
169
+ npx framer-dalton@latest session new "<projectUrl>" "<apiKey>" # First command: use @latest
170
+ npx framer-dalton docs Collection # Subsequent: @latest optional
171
+ \`\`\`
172
+
173
+ ### Required Workflow
174
+
175
+ Every task follows these steps:
176
+
177
+ #### 1. Connect (once per session)
178
+
179
+ Ask for the Project URL, then create a session:
180
+
181
+ \`\`\`bash
182
+ npx framer-dalton@latest session new "<projectUrl>" # Uses cached API key
183
+ npx framer-dalton@latest session new "<projectUrl>" "<apiKey>" # First time: needs API key
184
+ \`\`\`
185
+
186
+ #### 2. Look up the API (before EVERY code execution)
187
+
188
+ **You MUST run \`npx framer-dalton docs\` before writing any code.** Do not guess method names or signatures.
189
+
190
+ \`\`\`bash
191
+ npx framer-dalton docs Collection # What methods exist?
192
+ npx framer-dalton docs Collection.getItems # What are the parameters and return type?
193
+ \`\`\`
194
+
195
+ #### 3. Execute code
196
+
197
+ Only after checking docs:
198
+
199
+ \`\`\`bash
200
+ npx framer-dalton -s 1 -e "state.items = await collection.getItems(); console.log(state.items.length)"
201
+ \`\`\`
202
+
203
+ #### 4. Store results in \`state\`
204
+
205
+ Always save results you'll need again. Don't repeat API calls.
206
+
207
+ ---
208
+
209
+ **Do not skip step 2.** The examples below are patterns only - always verify current signatures with \`npx framer-dalton docs\`.
210
+
211
+ ### Communication style
212
+
213
+ Be concise. Don't narrate implementation details like field IDs, escaping, or internal steps. Just do the work and report what was accomplished in user-facing terms.
214
+
215
+ ---
216
+
217
+ ### Session Management
218
+
219
+ Each session maintains a persistent connection to a Framer project. Use sessions to:
220
+
221
+ - Keep state separate between different tasks
222
+ - Persist data across multiple execute calls
223
+ - Reuse the \`framer\` API instance without reconnecting
224
+
225
+ Get a new session ID:
226
+
227
+ \`\`\`bash
228
+ npx framer-dalton@latest session new "https://framer.com/projects/Website--abc123" "framer_api_xxx"
229
+ # outputs: 1
230
+ \`\`\`
231
+
232
+ **Always use your own session** - pass \`-s <id>\` to all commands. Using the same session preserves your \`state\` between calls.
233
+
234
+ List active sessions:
235
+
236
+ \`\`\`bash
237
+ npx framer-dalton session list
238
+ \`\`\`
239
+
240
+ ### Execute Code
241
+
242
+ \`\`\`bash
243
+ npx framer-dalton -s <sessionId> -e "<code>"
244
+ \`\`\`
245
+
246
+ Default timeout is 30 seconds. Increase with \`--timeout <ms>\`.
247
+
248
+ **Escaping:** For code with HTML, quotes, or special characters, use a heredoc (see below). For simple strings, use \`$'...'\` syntax.
249
+
250
+ **Examples:**
251
+
252
+ \`\`\`bash
253
+ # Fetch collections and store in state (always store results you'll reuse)
254
+ npx framer-dalton -s 1 -e "state.collections = await framer.getCollections(); console.log(state.collections.map(c => c.name))"
255
+
256
+ # Use stored data in subsequent calls
257
+ npx framer-dalton -s 1 -e "state.team = state.collections.find(c => c.name === 'Team')"
258
+ npx framer-dalton -s 1 -e "state.teamItems = await state.team.getItems(); console.log(state.teamItems.length)"
259
+ \`\`\`
260
+
261
+ **Multiline code with heredoc (recommended for complex strings):**
262
+
263
+ For code containing HTML, quotes, or special characters, use a heredoc to avoid escaping issues:
264
+
265
+ \`\`\`bash
266
+ npx framer-dalton -s 1 <<'EOF'
267
+ const translations = {
268
+ "node-id": "<h2>Ship's Treasures</h2>",
269
+ "other-id": "<p>Text with "quotes" and <tags></p>"
270
+ };
271
+ await framer.setLocalizationData({ valuesBySource: translations });
272
+ EOF
273
+ \`\`\`
274
+
275
+ The \`<<'EOF'\` syntax (with quotes around EOF) prevents shell interpolation.
276
+
277
+ **Alternative: pipe from file:**
278
+
279
+ \`\`\`bash
280
+ cat script.js | npx framer-dalton -s 1
281
+ \`\`\`
282
+
283
+ **Simple inline code:**
284
+
285
+ \`\`\`bash
286
+ npx framer-dalton -s 1 -e $'
287
+ const collections = await framer.getCollections();
288
+ for (const c of collections) {
289
+ const items = await c.getItems();
290
+ console.log(c.name, items.length);
291
+ }
292
+ '
293
+ \`\`\`
294
+
295
+ ### API Documentation
296
+
297
+ \`\`\`bash
298
+ npx framer-dalton docs # List all classes and methods
299
+ npx framer-dalton docs Collection # Show all methods for Collection
300
+ npx framer-dalton docs Collection.addItems # Show method with parameter types inlined
301
+ npx framer-dalton docs framer.screenshot # Show method signature and types
302
+ npx framer-dalton docs ScreenshotOptions # Look up a specific type definition
303
+ \`\`\`
304
+
305
+ When looking up a method, all referenced types are automatically expanded inline.
306
+
307
+ ---
308
+
309
+ ## Context Variables
310
+
311
+ - \`framer\` - Connected Framer Server API instance
312
+ - \`state\` - Object persisted between calls within your session
313
+ - \`console\` - For output (\`console.log\`, \`console.error\`)
314
+ - \`require\` - Sandboxed Node.js modules: fs, path, url, crypto, buffer, util, os
315
+ - Standard globals: \`fetch\`, \`Buffer\`, \`URL\`, \`crypto\`, \`setTimeout\`
316
+
317
+ **Note:** \`fs\` operations are sandboxed to cwd, /tmp, and os.tmpdir().
318
+
319
+ ### Use \`state\` to Avoid Repeated Calls
320
+
321
+ **Always store results in \`state\` when you'll need them again.** API calls are slow - don't repeat them.
322
+
323
+ \`\`\`bash
324
+ # First call: fetch and store
325
+ npx framer-dalton -s 1 -e "state.collections = await framer.getCollections()"
326
+
327
+ # Later calls: reuse from state
328
+ npx framer-dalton -s 1 -e "const team = state.collections.find(c => c.name === 'Team')"
329
+ npx framer-dalton -s 1 -e "state.teamItems = await state.collections.find(c => c.name === 'Team').getItems()"
330
+ \`\`\`
331
+
332
+ Store anything you'll reference again: collections, items, nodes, fields.
333
+
334
+ ---
335
+
336
+ ## API Examples
337
+
338
+ **STOP: These are patterns only. Before using any method below, run \`npx framer-dalton docs <ClassName>\` to verify the current signature.**
339
+
340
+ ---
341
+
342
+ ## Working with Collections (CMS)
343
+
344
+ Collections are Framer's CMS. Each collection has fields (columns) and items (rows).
345
+
346
+ ### Reading Collections
347
+
348
+ \`\`\`js
349
+ // Get all collections
350
+ const collections = await framer.getCollections();
351
+ console.log(collections.map((c) => ({ name: c.name, id: c.id })));
352
+
353
+ // Get a specific collection by ID
354
+ const collection = await framer.getCollection("collection-id");
355
+
356
+ // Get fields (columns) - returns array of { id, type, name }
357
+ const fields = await collection.getFields();
358
+ console.log(fields);
359
+ // [{ id: "BnNuS2i3o", type: "string", name: "Title" }, ...]
360
+
361
+ // Get items (rows)
362
+ const items = await collection.getItems();
363
+ console.log(items);
364
+ // [{ id: "XTM8FSHGs", slug: "post-1", draft: false, fieldData: {...} }, ...]
365
+ \`\`\`
366
+
367
+ ### Field Types
368
+
369
+ \`boolean\`, \`color\`, \`number\`, \`string\`, \`formattedText\` (HTML), \`image\`, \`file\`, \`link\`, \`date\`, \`enum\`, \`collectionReference\`, \`multiCollectionReference\`, \`array\` (galleries)
370
+
371
+ ### Updating Collection Items
372
+
373
+ \`\`\`js
374
+ // Add or update items (if id matches existing item, it updates)
375
+ await collection.addItems([
376
+ {
377
+ id: "new-item-1",
378
+ slug: "hello-world",
379
+ fieldData: { titleFieldId: "Hello World" },
380
+ },
381
+ ]);
382
+
383
+ // Remove items
384
+ await collection.removeItems(["item-id-1", "item-id-2"]);
385
+
386
+ // Reorder items
387
+ const ids = items.map((i) => i.id).reverse();
388
+ await collection.setItemOrder(ids);
389
+ \`\`\`
390
+
391
+ ### Working with CollectionItem
392
+
393
+ \`\`\`js
394
+ const items = await collection.getItems();
395
+ const item = items[0];
396
+
397
+ // Update item attributes
398
+ await item.setAttributes({ slug: "new-slug" });
399
+
400
+ // Navigate to item in Framer UI
401
+ await item.navigateTo();
402
+
403
+ // Remove item
404
+ await item.remove();
405
+ \`\`\`
406
+
407
+ ### Managed Collections
408
+
409
+ Managed collections are fully controlled by code - users can't edit them directly. Use for syncing external data sources.
410
+
411
+ \`\`\`js
412
+ // Create a managed collection
413
+ const managed = await framer.createManagedCollection({ name: "My Sync" });
414
+
415
+ // Set fields
416
+ await managed.setFields([
417
+ { id: "title", name: "Title", type: "string" },
418
+ { id: "content", name: "Content", type: "formattedText" },
419
+ { id: "published", name: "Published", type: "date" },
420
+ ]);
421
+
422
+ // Add items
423
+ await managed.addItems([
424
+ {
425
+ id: "1",
426
+ slug: "first-post",
427
+ fieldData: { title: "Hello", content: "<p>World</p>" },
428
+ },
429
+ ]);
430
+
431
+ // Store sync metadata
432
+ await managed.setPluginData("lastSync", new Date().toISOString());
433
+ const lastSync = await managed.getPluginData("lastSync");
434
+ \`\`\`
435
+
436
+ ---
437
+
438
+ ## Working with Nodes
439
+
440
+ Nodes are layers on the canvas: frames, text, images, components, etc.
441
+
442
+ ### Getting Nodes
443
+
444
+ \`\`\`js
445
+ // Get current selection
446
+ const selection = await framer.getSelection();
447
+
448
+ // Get canvas root
449
+ const root = await framer.getCanvasRoot();
450
+
451
+ // Get node by ID
452
+ const node = await framer.getNode("node-id");
453
+
454
+ // Get children of a node
455
+ const children = await framer.getChildren(node.id);
456
+
457
+ // Get parent
458
+ const parent = await framer.getParent(node.id);
459
+
460
+ // Find nodes by type
461
+ const frameNodes = await framer.getNodesWithType("FrameNode");
462
+ const textNodes = await framer.getNodesWithType("TextNode");
463
+
464
+ // Find nodes with specific attribute set
465
+ const nodesWithBg = await framer.getNodesWithAttributeSet("backgroundColor");
466
+ const nodesWithImage = await framer.getNodesWithAttributeSet("backgroundImage");
467
+ \`\`\`
468
+
469
+ ### Creating Nodes
470
+
471
+ \`\`\`js
472
+ // Create a frame
473
+ const frame = await framer.createFrameNode({
474
+ name: "My Frame",
475
+ width: 200,
476
+ height: 100,
477
+ backgroundColor: "rgba(255, 0, 0, 1)",
478
+ });
479
+
480
+ // Create text (creates TextNode, then use setText)
481
+ const textNode = await framer.createTextNode({ name: "My Text" });
482
+ await textNode.setText("Hello World");
483
+
484
+ // Add to specific parent
485
+ const child = await framer.createFrameNode({ name: "Child" }, parentNode.id);
486
+ \`\`\`
487
+
488
+ ### Modifying Nodes
489
+
490
+ \`\`\`js
491
+ // Update attributes
492
+ await node.setAttributes({
493
+ name: "New Name",
494
+ width: 300,
495
+ height: 200,
496
+ backgroundColor: "rgba(0, 0, 255, 0.5)",
497
+ visible: true,
498
+ locked: false,
499
+ });
500
+
501
+ // Move to different parent
502
+ await framer.setParent(node.id, newParentId);
503
+
504
+ // Clone a node
505
+ const clone = await node.clone();
506
+
507
+ // Remove nodes
508
+ await framer.removeNodes([node.id]);
509
+ \`\`\`
510
+
511
+ ### Working with Text
512
+
513
+ \`\`\`js
514
+ // Get text content
515
+ const text = await framer.getText(); // from selection
516
+ const nodeText = await textNode.getText();
517
+
518
+ // Set text
519
+ await framer.setText("New text"); // on selection
520
+ await textNode.setText("Hello World");
521
+ \`\`\`
522
+
523
+ ---
524
+
525
+ ## Working with Images
526
+
527
+ \`\`\`js
528
+ // Add image to canvas
529
+ await framer.addImage({
530
+ image: "https://example.com/image.png",
531
+ name: "My Image",
532
+ altText: "Description",
533
+ });
534
+
535
+ // Set image on selected node
536
+ await framer.setImage({
537
+ image: "https://example.com/image.png",
538
+ altText: "Description",
539
+ });
540
+
541
+ // Upload image without adding to canvas (for later use)
542
+ const imageAsset = await framer.uploadImage({
543
+ image: "https://example.com/image.png",
544
+ name: "My Image",
545
+ altText: "Alt text",
546
+ });
547
+
548
+ // Use uploaded image when creating a frame
549
+ await framer.createFrameNode({ backgroundImage: imageAsset });
550
+
551
+ // Get image from selection
552
+ const image = await framer.getImage();
553
+ console.log(image?.url);
554
+
555
+ // Add SVG (must be < 10kb)
556
+ await framer.addSVG({
557
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8"/></svg>',
558
+ name: "circle.svg",
559
+ });
560
+ \`\`\`
561
+
562
+ ---
563
+
564
+ ## Working with Styles
565
+
566
+ ### Color Styles
567
+
568
+ \`\`\`js
569
+ // List all color styles
570
+ const colorStyles = await framer.getColorStyles();
571
+
572
+ // Get specific style
573
+ const style = await framer.getColorStyle("style-id");
574
+
575
+ // Create color style (supports light/dark themes)
576
+ const newStyle = await framer.createColorStyle({
577
+ name: "Brand/Primary", // Use / for folders
578
+ light: "rgba(242, 59, 57, 1)",
579
+ dark: "rgba(180, 40, 40, 1)",
580
+ });
581
+
582
+ // Update style
583
+ await style.setAttributes({ light: "rgba(0, 100, 200, 1)" });
584
+
585
+ // Remove style
586
+ await style.remove();
587
+ \`\`\`
588
+
589
+ ### Text Styles
590
+
591
+ \`\`\`js
592
+ // List all text styles
593
+ const textStyles = await framer.getTextStyles();
594
+
595
+ // Create text style
596
+ const heading = await framer.createTextStyle({
597
+ name: "Typography/Heading 1",
598
+ tag: "h1",
599
+ fontSize: "48px",
600
+ lineHeight: "1.2em",
601
+ fontWeight: 700,
602
+ });
603
+
604
+ // With breakpoints for responsive typography
605
+ const responsive = await framer.createTextStyle({
606
+ fontSize: "24px",
607
+ minWidth: 1280,
608
+ breakpoints: [
609
+ { minWidth: 1024, fontSize: "20px" },
610
+ { minWidth: 768, fontSize: "18px" },
611
+ { minWidth: 320, fontSize: "16px" },
612
+ ],
613
+ });
614
+
615
+ // Update text style
616
+ await heading.setAttributes({ fontSize: "52px" });
617
+ \`\`\`
618
+
619
+ ### Fonts
620
+
621
+ \`\`\`js
622
+ // Get available fonts
623
+ const fonts = await framer.getFonts();
624
+
625
+ // Get specific font
626
+ const font = await framer.getFont("Inter", { weight: 400 });
627
+ const boldFont = await framer.getFont("Inter", { weight: 700 });
628
+
629
+ // Use font in text style
630
+ await framer.createTextStyle({
631
+ name: "Body",
632
+ font,
633
+ boldFont,
634
+ });
635
+ \`\`\`
636
+
637
+ ---
638
+
639
+ ## Working with Code Files
640
+
641
+ \`\`\`js
642
+ // List code files
643
+ const codeFiles = await framer.getCodeFiles();
644
+
645
+ // Get specific code file
646
+ const file = await framer.getCodeFile("file-id");
647
+
648
+ // Create code file
649
+ const newFile = await framer.createCodeFile({
650
+ name: "MyComponent.tsx",
651
+ content: \`export function MyComponent() { return <div>Hello</div> }\`,
652
+ });
653
+
654
+ // Type check code
655
+ const errors = await framer.typecheckCode(\`const x: string = 123\`);
656
+ \`\`\`
657
+
658
+ ---
659
+
660
+ ## Component Instances
661
+
662
+ \`\`\`js
663
+ // Insert a component by URL
664
+ await framer.addComponentInstance({
665
+ url: "https://framer.com/m/Button-5TDo.js",
666
+ attributes: {
667
+ width: "200px",
668
+ height: "50px",
669
+ controls: { label: "Click me", variant: "primary" },
670
+ },
671
+ });
672
+
673
+ // Insert as detached layers (editable)
674
+ await framer.addDetachedComponentLayers({
675
+ url: "https://framer.com/m/Card-abc.js",
676
+ attributes: { title: "My Card" },
677
+ });
678
+ \`\`\`
679
+
680
+ ---
681
+
682
+ ## Storing Data
683
+
684
+ Store metadata on nodes or globally in the project.
685
+
686
+ \`\`\`js
687
+ // Store global project data
688
+ await framer.setPluginData("myKey", "myValue");
689
+ const value = await framer.getPluginData("myKey");
690
+
691
+ // Store data on a node
692
+ await node.setPluginData("processed", "true");
693
+ const nodeData = await node.getPluginData("processed");
694
+
695
+ // List all keys
696
+ const keys = await framer.getPluginDataKeys();
697
+ const nodeKeys = await node.getPluginDataKeys();
698
+
699
+ // Delete data (set to null)
700
+ await framer.setPluginData("myKey", null);
701
+ \`\`\`
702
+
703
+ ---
704
+
705
+ ## Localization
706
+
707
+ \`\`\`js
708
+ // Get all locales
709
+ const locales = await framer.getLocales();
710
+ const defaultLocale = await framer.getDefaultLocale();
711
+
712
+ // Get localization groups (pages, CMS items with translations)
713
+ const groups = await framer.getLocalizationGroups();
714
+
715
+ // Update translations
716
+ const french = locales.find((l) => l.code === "fr");
717
+ await framer.setLocalizationData({
718
+ valuesBySource: {
719
+ [sourceId]: {
720
+ [french.id]: { action: "set", value: "Bonjour" },
721
+ },
722
+ },
723
+ });
724
+ \`\`\`
725
+
726
+ ---
727
+
728
+ ## Common Patterns
729
+
730
+ ### Iterate over all nodes in project
731
+
732
+ \`\`\`js
733
+ const root = await framer.getCanvasRoot();
734
+ for await (const node of root.walk()) {
735
+ console.log(node.name);
736
+ }
737
+ \`\`\`
738
+
739
+ ### Sync external data to collection
740
+
741
+ \`\`\`js
742
+ const collection = await framer.getManagedCollection();
743
+ const existingIds = new Set(await collection.getItemIds());
744
+
745
+ const externalData = await fetch("https://api.example.com/posts").then((r) =>
746
+ r.json(),
747
+ );
748
+
749
+ const items = externalData.map((post) => ({
750
+ id: post.id,
751
+ slug: post.slug,
752
+ fieldData: { title: post.title, content: post.body },
753
+ }));
754
+
755
+ await collection.addItems(items);
756
+
757
+ // Remove items no longer in external source
758
+ const newIds = new Set(items.map((i) => i.id));
759
+ const toRemove = [...existingIds].filter((id) => !newIds.has(id));
760
+ if (toRemove.length) await collection.removeItems(toRemove);
761
+
762
+ await collection.setPluginData("lastSync", new Date().toISOString());
763
+ \`\`\`
764
+
765
+ ### Batch update all images' alt text
766
+
767
+ \`\`\`js
768
+ const nodes = await framer.getNodesWithAttributeSet("backgroundImage");
769
+ for (const node of nodes) {
770
+ if (!node.backgroundImage) continue;
771
+ await node.setAttributes({
772
+ backgroundImage: node.backgroundImage.cloneWithAttributes({
773
+ altText: "Updated description",
774
+ }),
775
+ });
776
+ }
777
+ \`\`\`
778
+
779
+ ---
780
+
781
+ ## Screenshots and SVG Export
782
+
783
+ Server API exclusive methods for capturing visual output from nodes.
784
+
785
+ \`\`\`js
786
+ // Take a screenshot of a node (returns Buffer + mimeType)
787
+ const result = await framer.screenshot(node.id);
788
+ console.log(result.mimeType); // "image/png"
789
+ await require("fs").promises.writeFile("/tmp/screenshot.png", result.data);
790
+
791
+ // With options
792
+ const jpg = await framer.screenshot(node.id, {
793
+ format: "jpeg", // "png" (default) or "jpeg"
794
+ quality: 90, // JPEG quality 0-100 (default 100)
795
+ scale: 2, // Pixel density: 0.5, 1, 1.5, 2, 3, or 4 (default 1)
796
+ clip: { x: 0, y: 0, width: 100, height: 100 }, // Clip region in CSS pixels
797
+ });
798
+
799
+ // Export as SVG string
800
+ const svgString = await framer.exportSVG(node.id);
801
+ await require("fs").promises.writeFile("/tmp/export.svg", svgString);
802
+ \`\`\`
803
+
804
+ ---
805
+
806
+ ## Publishing and Deployments
807
+
808
+ Server API exclusive methods for publishing and managing deployments.
809
+
810
+ \`\`\`js
811
+ // Publish the project (creates a new deployment)
812
+ const result = await framer.publish();
813
+ console.log(result.deployment.id); // Deployment ID
814
+ console.log(result.hostnames); // Array of hostnames
815
+
816
+ // Get all deployments
817
+ const deployments = await framer.getDeployments();
818
+ for (const d of deployments) {
819
+ console.log(d.id, d.createdAt);
820
+ }
821
+
822
+ // Deploy a specific deployment to domains
823
+ const hostnames = await framer.deploy(deploymentId, ["example.com"]);
824
+
825
+ // Get current publish info (production and staging URLs)
826
+ const info = await framer.getPublishInfo();
827
+ console.log(info.production?.url); // Production URL
828
+ console.log(info.staging?.url); // Staging URL
829
+ \`\`\`
830
+
831
+ ---
832
+
833
+ ## Change Tracking
834
+
835
+ Server API exclusive methods for tracking project changes.
836
+
837
+ \`\`\`js
838
+ // Get changed paths since last publish
839
+ const changes = await framer.getChangedPaths();
840
+ console.log(changes.added); // New paths
841
+ console.log(changes.modified); // Modified paths
842
+ console.log(changes.removed); // Removed paths
843
+
844
+ // Get contributors to changes (returns user IDs)
845
+ const contributors = await framer.getChangeContributors();
846
+ \`\`\`
847
+
848
+ ---
849
+
850
+ ## Capabilities
851
+
852
+ What you can do with the Framer CLI:
853
+
854
+ - **CMS**: Create, read, update, delete collections and items. Sync external databases.
855
+ - **Canvas**: Create and modify frames, text, images, SVGs. Build layouts programmatically.
856
+ - **Styles**: Manage color and text styles. Sync design systems.
857
+ - **Components**: Insert component instances with configured properties.
858
+ - **Code**: Create and manage code component files.
859
+ - **Assets**: Upload and manage images and files.
860
+ - **Localization**: Manage translations programmatically.
861
+ - **Data**: Store metadata on nodes and projects for plugin state.
862
+ - **Screenshots**: Capture node screenshots as PNG/JPEG. Export nodes as SVG.
863
+ - **Publishing**: Publish projects, manage deployments, track changes.
864
+ `;
865
+
163
866
  // src/types-data.ts
164
867
  var methods = [
165
868
  {
@@ -6299,6 +7002,9 @@ function getReferencedTypes(text, seen = /* @__PURE__ */ new Set()) {
6299
7002
  return result;
6300
7003
  }
6301
7004
  __name(getReferencedTypes, "getReferencedTypes");
7005
+ program.command("skill").description("Output complete skill documentation for Claude").action(() => {
7006
+ print(skillDocs);
7007
+ });
6302
7008
  program.command("docs [queries...]").description(
6303
7009
  "Look up API documentation. Use: docs, docs ClassName, docs Class.method, or docs TypeName"
6304
7010
  ).action((queries) => {
@@ -9,7 +9,7 @@ import { createRequire } from 'module';
9
9
  import * as vm from 'vm';
10
10
  import { connect } from 'framer-api';
11
11
 
12
- /* @framer/ai relay server v0.0.1 */
12
+ /* @framer/ai relay server v0.0.2 */
13
13
  var __defProp = Object.defineProperty;
14
14
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
15
  function getLogPath() {
@@ -46,7 +46,7 @@ function log(message) {
46
46
  __name(log, "log");
47
47
  var __filename$1 = fileURLToPath(import.meta.url);
48
48
  path.dirname(__filename$1);
49
- var VERSION = "0.0.1" ;
49
+ var VERSION = "0.0.2" ;
50
50
  var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19987;
51
51
 
52
52
  // src/connection-errors.ts
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "framer-dalton",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "bin": {
6
- "framer": "./dist/cli.js"
6
+ "framer-dalton": "./dist/cli.js"
7
7
  },
8
8
  "main": "./dist/cli.js",
9
9
  "types": "./dist/cli.d.ts",