@framer/agent 0.0.35 → 0.0.37
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/README.md +1 -1
- package/dist/cli.js +489 -205
- package/dist/start-relay-server.js +55 -2
- package/package.json +5 -5
- package/skills/framer/SKILL.md +198 -0
- package/skills/framer/projects/__template__/index.template.md +35 -0
- package/skills/framer/projects/__template__/project-inventory.template.md +27 -0
- package/skills/framer/projects/__template__/recipes.md +119 -0
- package/skills/framer/start-conversation.md +33 -0
- package/{docs/skills/framer-code-components.md → skills/framer-code-components/SKILL.md} +1 -1
- package/docs/skills/framer-project.md +0 -479
- package/docs/skills/framer.md +0 -76
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs8 from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import path6 from 'path';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import crypto, { randomUUID } from 'crypto';
|
|
6
6
|
import http from 'http';
|
|
@@ -9,11 +9,11 @@ import { z } from 'zod';
|
|
|
9
9
|
import os2 from 'os';
|
|
10
10
|
import isWsl from 'is-wsl';
|
|
11
11
|
import { ErrorCode, FramerAPIError } from 'framer-api';
|
|
12
|
-
import net from 'net';
|
|
13
12
|
import { fileURLToPath } from 'url';
|
|
13
|
+
import net from 'net';
|
|
14
14
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
15
15
|
|
|
16
|
-
/* @framer/ai CLI v0.0.
|
|
16
|
+
/* @framer/ai CLI v0.0.37 */
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
18
18
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
19
19
|
|
|
@@ -85,20 +85,19 @@ function resolveAgentThreadContext(env = process.env) {
|
|
|
85
85
|
return {};
|
|
86
86
|
}
|
|
87
87
|
__name(resolveAgentThreadContext, "resolveAgentThreadContext");
|
|
88
|
-
function
|
|
88
|
+
function getOpenUrlCommand(url) {
|
|
89
89
|
const platform = process.platform;
|
|
90
|
-
let cmd;
|
|
91
|
-
let args;
|
|
92
90
|
if (platform === "darwin") {
|
|
93
|
-
cmd
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
cmd
|
|
97
|
-
args = ["/c", "start", "", `"${url}"`];
|
|
98
|
-
} else {
|
|
99
|
-
cmd = "xdg-open";
|
|
100
|
-
args = [url];
|
|
91
|
+
return { cmd: "open", args: [url] };
|
|
92
|
+
}
|
|
93
|
+
if (platform === "win32") {
|
|
94
|
+
return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
|
|
101
95
|
}
|
|
96
|
+
return { cmd: "xdg-open", args: [url] };
|
|
97
|
+
}
|
|
98
|
+
__name(getOpenUrlCommand, "getOpenUrlCommand");
|
|
99
|
+
function openUrl(url) {
|
|
100
|
+
const { cmd, args } = getOpenUrlCommand(url);
|
|
102
101
|
return new Promise((resolve) => {
|
|
103
102
|
const child = execFile(cmd, args);
|
|
104
103
|
child.on("error", () => resolve(false));
|
|
@@ -108,12 +107,12 @@ function openUrl(url) {
|
|
|
108
107
|
__name(openUrl, "openUrl");
|
|
109
108
|
function getConfigDir() {
|
|
110
109
|
if (process.env.XDG_CONFIG_HOME) {
|
|
111
|
-
return
|
|
110
|
+
return path6.join(process.env.XDG_CONFIG_HOME, "framer");
|
|
112
111
|
}
|
|
113
112
|
if (process.platform === "win32") {
|
|
114
|
-
return
|
|
113
|
+
return path6.join(process.env.APPDATA || os2.homedir(), "framer");
|
|
115
114
|
}
|
|
116
|
-
return
|
|
115
|
+
return path6.join(os2.homedir(), ".config", "framer");
|
|
117
116
|
}
|
|
118
117
|
__name(getConfigDir, "getConfigDir");
|
|
119
118
|
function ensureConfigDir() {
|
|
@@ -124,11 +123,11 @@ __name(ensureConfigDir, "ensureConfigDir");
|
|
|
124
123
|
|
|
125
124
|
// src/config/projects.ts
|
|
126
125
|
function getProjectsConfigPath() {
|
|
127
|
-
return
|
|
126
|
+
return path6.join(getConfigDir(), "projects.json");
|
|
128
127
|
}
|
|
129
128
|
__name(getProjectsConfigPath, "getProjectsConfigPath");
|
|
130
129
|
function getLegacyCredentialsPath() {
|
|
131
|
-
return
|
|
130
|
+
return path6.join(getConfigDir(), "credentials.json");
|
|
132
131
|
}
|
|
133
132
|
__name(getLegacyCredentialsPath, "getLegacyCredentialsPath");
|
|
134
133
|
var ProjectsConfigSchema = z.object({
|
|
@@ -314,7 +313,7 @@ var SettingsFileSchema = z.object({
|
|
|
314
313
|
telemetryNoticeShown: z.boolean().optional()
|
|
315
314
|
});
|
|
316
315
|
function getSettingsPath() {
|
|
317
|
-
return
|
|
316
|
+
return path6.join(getConfigDir(), "settings.json");
|
|
318
317
|
}
|
|
319
318
|
__name(getSettingsPath, "getSettingsPath");
|
|
320
319
|
var settings;
|
|
@@ -398,7 +397,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
398
397
|
// src/version.ts
|
|
399
398
|
var VERSION = (
|
|
400
399
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
401
|
-
"0.0.
|
|
400
|
+
"0.0.37"
|
|
402
401
|
);
|
|
403
402
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
404
403
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -1175,7 +1174,7 @@ var types = {
|
|
|
1175
1174
|
members: [
|
|
1176
1175
|
{
|
|
1177
1176
|
name: "type",
|
|
1178
|
-
type: '"page" | "collection" | "designPage" | "siteSettings" | "component" | "layoutTemplate" | "vectorSet" | "codeFile"',
|
|
1177
|
+
type: '"page" | "collection" | "designPage" | "siteSettings" | "component" | "layoutTemplate" | "vectorSet" | "codeFile" | "colorStyle" | "textStyle" | "linkStyle" | "blockquoteStyle" | "inlineCodeStyle" | "localization" | "localizationGlossary"',
|
|
1179
1178
|
description: "",
|
|
1180
1179
|
optional: false
|
|
1181
1180
|
},
|
|
@@ -7628,6 +7627,12 @@ var types = {
|
|
|
7628
7627
|
description: "@alpha",
|
|
7629
7628
|
optional: false
|
|
7630
7629
|
},
|
|
7630
|
+
{
|
|
7631
|
+
name: "replaceTextForAgent",
|
|
7632
|
+
type: "(input: {\n id: string;\n searchText: string;\n replaceText: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<boolean>",
|
|
7633
|
+
description: "@alpha",
|
|
7634
|
+
optional: false
|
|
7635
|
+
},
|
|
7631
7636
|
{
|
|
7632
7637
|
name: "ping",
|
|
7633
7638
|
type: "() => Promise<void>",
|
|
@@ -7682,6 +7687,24 @@ var types = {
|
|
|
7682
7687
|
description: "@alpha",
|
|
7683
7688
|
optional: false
|
|
7684
7689
|
},
|
|
7690
|
+
{
|
|
7691
|
+
name: "getDescendantsOfTypesForAgent",
|
|
7692
|
+
type: "(input: {\n id: string;\n types: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7693
|
+
description: "@alpha",
|
|
7694
|
+
optional: false
|
|
7695
|
+
},
|
|
7696
|
+
{
|
|
7697
|
+
name: "getRectForAgent",
|
|
7698
|
+
type: "(input: {\n id: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<Rect$1 | null>",
|
|
7699
|
+
description: "@alpha",
|
|
7700
|
+
optional: false
|
|
7701
|
+
},
|
|
7702
|
+
{
|
|
7703
|
+
name: "getDescendantReferencesOfTypesForAgent",
|
|
7704
|
+
type: "(input: {\n id: string;\n types: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7705
|
+
description: "@alpha",
|
|
7706
|
+
optional: false
|
|
7707
|
+
},
|
|
7685
7708
|
{
|
|
7686
7709
|
name: "getScopeNodeForAgent",
|
|
7687
7710
|
type: "(input: {\n id: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
@@ -14249,6 +14272,20 @@ var methodsByCategory = {
|
|
|
14249
14272
|
description: "Returns the dynamic project context as a string.\n\nThe context includes project-specific data:\n- **Available fonts** \u2014 font families loaded in the project.\n- **Components** \u2014 component names and their controls.\n- **Design tokens** \u2014 color tokens defined in the project.\n- **Style presets** \u2014 text style presets defined in the project.\n- **Icon sets** \u2014 available icon sets and their definitions.\n\nThis data changes per project. Pair with the static prompt\nfrom {@link getSystemPrompt} for complete agent context.\n\n@returns A string containing the project context.",
|
|
14250
14273
|
references: []
|
|
14251
14274
|
},
|
|
14275
|
+
{
|
|
14276
|
+
name: "getDescendantReferencesOfTypes",
|
|
14277
|
+
category: "FramerAgentAPI",
|
|
14278
|
+
signature: "getDescendantReferencesOfTypes(input: { id: string; types: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14279
|
+
description: 'Get style and token nodes referenced by a node\'s descendants.\n\n@param input - `{ id, types }`: the ancestor node id and referenced node kinds to match.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The referenced nodes without their children.',
|
|
14280
|
+
references: []
|
|
14281
|
+
},
|
|
14282
|
+
{
|
|
14283
|
+
name: "getDescendantsOfTypes",
|
|
14284
|
+
category: "FramerAgentAPI",
|
|
14285
|
+
signature: "getDescendantsOfTypes(input: { id: string; types: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14286
|
+
description: 'Get descendants of a node with one or more kinds (e.g. `"FrameNode"`, `"RichTextNode"`, `"TextRun"`).\n\n@param input - `{ id, types }`: the ancestor node id and node kinds to match.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The matching descendants without their children.',
|
|
14287
|
+
references: []
|
|
14288
|
+
},
|
|
14252
14289
|
{
|
|
14253
14290
|
name: "getGroundNode",
|
|
14254
14291
|
category: "FramerAgentAPI",
|
|
@@ -14284,6 +14321,13 @@ var methodsByCategory = {
|
|
|
14284
14321
|
description: 'Get the direct parent of a node.\n\n@param input - `{ id }`: the id of the child node.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The parent node, or `null` if the node has no parent or doesn\'t exist.',
|
|
14285
14322
|
references: []
|
|
14286
14323
|
},
|
|
14324
|
+
{
|
|
14325
|
+
name: "getRect",
|
|
14326
|
+
category: "FramerAgentAPI",
|
|
14327
|
+
signature: "getRect(input: { id: string; }, options?: { pagePath?: string; }): Promise<Rect$1 | null>",
|
|
14328
|
+
description: 'Get the measured rect for a node.\n\n@param input - `{ id }`: the id of the node to measure.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The rounded measured rect, or `null` if no node with that id exists on the page.',
|
|
14329
|
+
references: ["Rect$1"]
|
|
14330
|
+
},
|
|
14287
14331
|
{
|
|
14288
14332
|
name: "getScopeNode",
|
|
14289
14333
|
category: "FramerAgentAPI",
|
|
@@ -14396,6 +14440,13 @@ var methodsByCategory = {
|
|
|
14396
14440
|
description: "Renames a branch.\n\n@param branchId - Branch id to rename.\n@param title - New branch title.",
|
|
14397
14441
|
references: []
|
|
14398
14442
|
},
|
|
14443
|
+
{
|
|
14444
|
+
name: "replaceText",
|
|
14445
|
+
category: "FramerAgentAPI",
|
|
14446
|
+
signature: "replaceText(input: { id: string; searchText: string; replaceText: string; }, options?: { pagePath?: string; }): Promise<boolean>",
|
|
14447
|
+
description: 'Replaces text within a text-like node.\n\n@param input - `{ id, searchText, replaceText }`: the target node id, text to find, and replacement text.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns `true` when text was replaced, or `false` when no matching text was found.',
|
|
14448
|
+
references: []
|
|
14449
|
+
},
|
|
14399
14450
|
{
|
|
14400
14451
|
name: "reviewChanges",
|
|
14401
14452
|
category: "FramerAgentAPI",
|
|
@@ -16619,6 +16670,391 @@ ${typeDef}`);
|
|
|
16619
16670
|
}
|
|
16620
16671
|
__name(renderDocs, "renderDocs");
|
|
16621
16672
|
|
|
16673
|
+
// src/prompt-context.ts
|
|
16674
|
+
function toKebabId(title) {
|
|
16675
|
+
const id = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16676
|
+
return id || "section";
|
|
16677
|
+
}
|
|
16678
|
+
__name(toKebabId, "toKebabId");
|
|
16679
|
+
function uniquifyId(id, seen) {
|
|
16680
|
+
const count = seen.get(id) ?? 0;
|
|
16681
|
+
seen.set(id, count + 1);
|
|
16682
|
+
return count === 0 ? id : `${id}-${count + 1}`;
|
|
16683
|
+
}
|
|
16684
|
+
__name(uniquifyId, "uniquifyId");
|
|
16685
|
+
function splitPromptSections(markdown) {
|
|
16686
|
+
const normalized = markdown.replaceAll("\r\n", "\n");
|
|
16687
|
+
if (normalized.trim() === "") {
|
|
16688
|
+
throw new Error("Cannot split an empty agent prompt.");
|
|
16689
|
+
}
|
|
16690
|
+
const sections = [];
|
|
16691
|
+
let current;
|
|
16692
|
+
for (const line of normalized.split("\n")) {
|
|
16693
|
+
const heading = line.match(/^#\s+(.+?)\s*#*\s*$/u);
|
|
16694
|
+
if (heading) {
|
|
16695
|
+
current = { title: heading[1].trim(), lines: [] };
|
|
16696
|
+
sections.push(current);
|
|
16697
|
+
continue;
|
|
16698
|
+
}
|
|
16699
|
+
if (!current) {
|
|
16700
|
+
current = { title: "Preamble", lines: [] };
|
|
16701
|
+
sections.push(current);
|
|
16702
|
+
}
|
|
16703
|
+
current.lines.push(line);
|
|
16704
|
+
}
|
|
16705
|
+
const seen = /* @__PURE__ */ new Map();
|
|
16706
|
+
return sections.map((section) => ({
|
|
16707
|
+
id: uniquifyId(toKebabId(section.title), seen),
|
|
16708
|
+
title: section.title,
|
|
16709
|
+
content: section.lines.join("\n").trim()
|
|
16710
|
+
})).filter(
|
|
16711
|
+
(section) => section.title !== "Preamble" || section.content !== ""
|
|
16712
|
+
);
|
|
16713
|
+
}
|
|
16714
|
+
__name(splitPromptSections, "splitPromptSections");
|
|
16715
|
+
function renderTemplate(template, values) {
|
|
16716
|
+
let rendered = template;
|
|
16717
|
+
for (const [key, value] of Object.entries(values)) {
|
|
16718
|
+
rendered = rendered.split(`{{${key}}}`).join(value);
|
|
16719
|
+
}
|
|
16720
|
+
return rendered;
|
|
16721
|
+
}
|
|
16722
|
+
__name(renderTemplate, "renderTemplate");
|
|
16723
|
+
function writeRenderedFile(filePath, content) {
|
|
16724
|
+
fs8.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
16725
|
+
fs8.writeFileSync(filePath, `${content.trimEnd()}
|
|
16726
|
+
`, "utf-8");
|
|
16727
|
+
}
|
|
16728
|
+
__name(writeRenderedFile, "writeRenderedFile");
|
|
16729
|
+
function isTextFile(filePath) {
|
|
16730
|
+
const extension = path6.extname(filePath).toLowerCase();
|
|
16731
|
+
return [
|
|
16732
|
+
".css",
|
|
16733
|
+
".html",
|
|
16734
|
+
".js",
|
|
16735
|
+
".json",
|
|
16736
|
+
".md",
|
|
16737
|
+
".mjs",
|
|
16738
|
+
".ts",
|
|
16739
|
+
".tsx",
|
|
16740
|
+
".txt",
|
|
16741
|
+
".yaml",
|
|
16742
|
+
".yml"
|
|
16743
|
+
].includes(extension);
|
|
16744
|
+
}
|
|
16745
|
+
__name(isTextFile, "isTextFile");
|
|
16746
|
+
function renderFileTree(sourceDir, destinationDir, options) {
|
|
16747
|
+
for (const entry of fs8.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
16748
|
+
const sourcePath = path6.join(sourceDir, entry.name);
|
|
16749
|
+
const destinationPath = path6.join(destinationDir, entry.name);
|
|
16750
|
+
if (entry.isDirectory()) {
|
|
16751
|
+
fs8.mkdirSync(destinationPath, { recursive: true });
|
|
16752
|
+
renderFileTree(sourcePath, destinationPath, options);
|
|
16753
|
+
continue;
|
|
16754
|
+
}
|
|
16755
|
+
if (entry.isSymbolicLink()) {
|
|
16756
|
+
throw new Error(options.getSymlinkErrorMessage(sourcePath));
|
|
16757
|
+
}
|
|
16758
|
+
const renderedDestinationPath = options.getRenderedDestinationPath({
|
|
16759
|
+
sourcePath,
|
|
16760
|
+
destinationPath
|
|
16761
|
+
});
|
|
16762
|
+
if (renderedDestinationPath) {
|
|
16763
|
+
const renderedContent = renderTemplate(
|
|
16764
|
+
fs8.readFileSync(sourcePath, "utf-8"),
|
|
16765
|
+
options.templateValues
|
|
16766
|
+
);
|
|
16767
|
+
writeRenderedFile(
|
|
16768
|
+
renderedDestinationPath,
|
|
16769
|
+
options.transformRenderedContent?.(renderedContent) ?? renderedContent
|
|
16770
|
+
);
|
|
16771
|
+
continue;
|
|
16772
|
+
}
|
|
16773
|
+
fs8.copyFileSync(sourcePath, destinationPath);
|
|
16774
|
+
}
|
|
16775
|
+
}
|
|
16776
|
+
__name(renderFileTree, "renderFileTree");
|
|
16777
|
+
var FRAMER_TEMPORARY_DIR = path6.join(os2.tmpdir(), "framer");
|
|
16778
|
+
function ensureTemporaryDir() {
|
|
16779
|
+
fs8.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
|
|
16780
|
+
}
|
|
16781
|
+
__name(ensureTemporaryDir, "ensureTemporaryDir");
|
|
16782
|
+
function cleanupStaleSessionCodeFiles(activeSessionIds) {
|
|
16783
|
+
if (!fs8.existsSync(FRAMER_TEMPORARY_DIR)) return;
|
|
16784
|
+
const activeSessionIdsSet = new Set(activeSessionIds);
|
|
16785
|
+
for (const entry of fs8.readdirSync(FRAMER_TEMPORARY_DIR)) {
|
|
16786
|
+
const [sessionId] = entry.split("-");
|
|
16787
|
+
if (activeSessionIdsSet.has(sessionId)) continue;
|
|
16788
|
+
const filePath = path6.join(FRAMER_TEMPORARY_DIR, entry);
|
|
16789
|
+
fs8.rmSync(filePath, { recursive: true, force: true, maxRetries: 2 });
|
|
16790
|
+
}
|
|
16791
|
+
}
|
|
16792
|
+
__name(cleanupStaleSessionCodeFiles, "cleanupStaleSessionCodeFiles");
|
|
16793
|
+
|
|
16794
|
+
// src/skills.ts
|
|
16795
|
+
var META_SKILL_NAME = "framer";
|
|
16796
|
+
var CODE_COMPONENTS_SKILL_NAME = "framer-code-components";
|
|
16797
|
+
var __dirname$1 = path6.dirname(fileURLToPath(import.meta.url));
|
|
16798
|
+
var sourceSkillsDir = path6.join(__dirname$1, "..", "skills");
|
|
16799
|
+
var GENERATED_PROJECTS_DIR_NAME = "projects";
|
|
16800
|
+
var GENERATED_PROJECT_TEMPLATE_ID = "__template__";
|
|
16801
|
+
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16802
|
+
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16803
|
+
var PACKAGE_NAME = "@framer/agent";
|
|
16804
|
+
var DEFAULT_FRAMER_AGENT_COMMAND = `npx ${PACKAGE_NAME}`;
|
|
16805
|
+
var RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND = `npx --min-release-age=0 ${PACKAGE_NAME}`;
|
|
16806
|
+
function getFramerAgentCommand() {
|
|
16807
|
+
return isNpxMinReleaseAgeOverrideEnabled() ? RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND : DEFAULT_FRAMER_AGENT_COMMAND;
|
|
16808
|
+
}
|
|
16809
|
+
__name(getFramerAgentCommand, "getFramerAgentCommand");
|
|
16810
|
+
function replaceDefaultFramerAgentCommand(content) {
|
|
16811
|
+
return content.replaceAll(
|
|
16812
|
+
DEFAULT_FRAMER_AGENT_COMMAND,
|
|
16813
|
+
getFramerAgentCommand()
|
|
16814
|
+
);
|
|
16815
|
+
}
|
|
16816
|
+
__name(replaceDefaultFramerAgentCommand, "replaceDefaultFramerAgentCommand");
|
|
16817
|
+
function getSkillTemplateValues(skillDir) {
|
|
16818
|
+
return {
|
|
16819
|
+
FRAMER_TEMPORARY_DIR,
|
|
16820
|
+
FRAMER_AGENT_COMMAND: getFramerAgentCommand(),
|
|
16821
|
+
FRAMER_SKILL_DIR: skillDir
|
|
16822
|
+
};
|
|
16823
|
+
}
|
|
16824
|
+
__name(getSkillTemplateValues, "getSkillTemplateValues");
|
|
16825
|
+
function toSafeProjectId(projectId) {
|
|
16826
|
+
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16827
|
+
}
|
|
16828
|
+
__name(toSafeProjectId, "toSafeProjectId");
|
|
16829
|
+
function removeSkillDirContents(skillDir, options) {
|
|
16830
|
+
for (const entry of fs8.readdirSync(skillDir, { withFileTypes: true })) {
|
|
16831
|
+
const entryPath = path6.join(skillDir, entry.name);
|
|
16832
|
+
if (options.preserveGeneratedProjectBundles && entry.name === GENERATED_PROJECTS_DIR_NAME && entry.isDirectory()) {
|
|
16833
|
+
fs8.rmSync(path6.join(entryPath, GENERATED_PROJECT_TEMPLATE_ID), {
|
|
16834
|
+
recursive: true,
|
|
16835
|
+
force: true,
|
|
16836
|
+
maxRetries: 2
|
|
16837
|
+
});
|
|
16838
|
+
continue;
|
|
16839
|
+
}
|
|
16840
|
+
fs8.rmSync(entryPath, { recursive: true, force: true, maxRetries: 2 });
|
|
16841
|
+
}
|
|
16842
|
+
}
|
|
16843
|
+
__name(removeSkillDirContents, "removeSkillDirContents");
|
|
16844
|
+
function prepareSkillDir(root, skillName) {
|
|
16845
|
+
fs8.mkdirSync(root, { recursive: true });
|
|
16846
|
+
const rootStat = fs8.statSync(root);
|
|
16847
|
+
if (!rootStat.isDirectory()) {
|
|
16848
|
+
throw new Error(`Skill root is not a directory: ${root}`);
|
|
16849
|
+
}
|
|
16850
|
+
const skillDir = path6.join(root, skillName);
|
|
16851
|
+
const current = fs8.lstatSync(skillDir, { throwIfNoEntry: false });
|
|
16852
|
+
if (current && (current.isSymbolicLink() || !current.isDirectory())) {
|
|
16853
|
+
fs8.rmSync(skillDir, { recursive: true, force: true });
|
|
16854
|
+
}
|
|
16855
|
+
if (current?.isDirectory()) {
|
|
16856
|
+
removeSkillDirContents(skillDir, {
|
|
16857
|
+
preserveGeneratedProjectBundles: skillName === META_SKILL_NAME
|
|
16858
|
+
});
|
|
16859
|
+
}
|
|
16860
|
+
fs8.mkdirSync(skillDir, { recursive: true });
|
|
16861
|
+
return skillDir;
|
|
16862
|
+
}
|
|
16863
|
+
__name(prepareSkillDir, "prepareSkillDir");
|
|
16864
|
+
function installSourceSkill(root, skillName) {
|
|
16865
|
+
const sourceDir = path6.join(sourceSkillsDir, skillName);
|
|
16866
|
+
const skillDir = prepareSkillDir(root, skillName);
|
|
16867
|
+
renderFileTree(sourceDir, skillDir, {
|
|
16868
|
+
templateValues: getSkillTemplateValues(skillDir),
|
|
16869
|
+
getRenderedDestinationPath: /* @__PURE__ */ __name(({ sourcePath, destinationPath }) => isTextFile(sourcePath) ? destinationPath : void 0, "getRenderedDestinationPath"),
|
|
16870
|
+
getSymlinkErrorMessage: /* @__PURE__ */ __name((sourcePath) => `Source skill contains unsupported symlink: ${sourcePath}`, "getSymlinkErrorMessage")
|
|
16871
|
+
});
|
|
16872
|
+
return path6.join(skillDir, "SKILL.md");
|
|
16873
|
+
}
|
|
16874
|
+
__name(installSourceSkill, "installSourceSkill");
|
|
16875
|
+
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16876
|
+
for (const root of skillRoots) {
|
|
16877
|
+
if (!fs8.existsSync(root)) continue;
|
|
16878
|
+
for (const entry of fs8.readdirSync(root)) {
|
|
16879
|
+
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16880
|
+
const skillDir = path6.join(root, entry);
|
|
16881
|
+
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16882
|
+
}
|
|
16883
|
+
}
|
|
16884
|
+
}
|
|
16885
|
+
__name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
|
|
16886
|
+
function getDefaultSkillRoots() {
|
|
16887
|
+
const home = os2.homedir();
|
|
16888
|
+
return [
|
|
16889
|
+
path6.join(home, ".agents", "skills"),
|
|
16890
|
+
path6.join(home, ".claude", "skills")
|
|
16891
|
+
];
|
|
16892
|
+
}
|
|
16893
|
+
__name(getDefaultSkillRoots, "getDefaultSkillRoots");
|
|
16894
|
+
function cleanupProjectSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16895
|
+
for (const root of skillRoots) {
|
|
16896
|
+
if (!fs8.existsSync(root)) continue;
|
|
16897
|
+
for (const entry of fs8.readdirSync(root)) {
|
|
16898
|
+
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16899
|
+
const skillDir = path6.join(root, entry);
|
|
16900
|
+
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16901
|
+
}
|
|
16902
|
+
}
|
|
16903
|
+
}
|
|
16904
|
+
__name(cleanupProjectSkills, "cleanupProjectSkills");
|
|
16905
|
+
function installSkills(options = { type: "base" }) {
|
|
16906
|
+
const skillRoots = options.skillRoots ?? getDefaultSkillRoots();
|
|
16907
|
+
const skillNames = [META_SKILL_NAME, CODE_COMPONENTS_SKILL_NAME];
|
|
16908
|
+
const results = skillNames.map((skillName) => ({
|
|
16909
|
+
skillName,
|
|
16910
|
+
paths: skillRoots.map((root) => installSourceSkill(root, skillName))
|
|
16911
|
+
}));
|
|
16912
|
+
trackSkillsInstall({
|
|
16913
|
+
skillType: options.type,
|
|
16914
|
+
skillCount: results.length
|
|
16915
|
+
});
|
|
16916
|
+
return results;
|
|
16917
|
+
}
|
|
16918
|
+
__name(installSkills, "installSkills");
|
|
16919
|
+
|
|
16920
|
+
// src/generated-project-bundle.ts
|
|
16921
|
+
var PROJECT_BUNDLE_SCHEMA_VERSION = 1;
|
|
16922
|
+
var PROJECT_TEMPLATE_ID = "__template__";
|
|
16923
|
+
var TEMPLATE_FILE_MARKER = ".template";
|
|
16924
|
+
var __dirname2 = path6.dirname(fileURLToPath(import.meta.url));
|
|
16925
|
+
function sha256(content) {
|
|
16926
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
16927
|
+
}
|
|
16928
|
+
__name(sha256, "sha256");
|
|
16929
|
+
function renderPromptFile(section) {
|
|
16930
|
+
return [`# ${section.title}`, "", section.content].join("\n");
|
|
16931
|
+
}
|
|
16932
|
+
__name(renderPromptFile, "renderPromptFile");
|
|
16933
|
+
function getRenderedTemplatePath(filePath) {
|
|
16934
|
+
const extension = path6.extname(filePath);
|
|
16935
|
+
const suffix = `${TEMPLATE_FILE_MARKER}${extension}`;
|
|
16936
|
+
if (!filePath.endsWith(suffix)) return void 0;
|
|
16937
|
+
return filePath.slice(0, -suffix.length) + extension;
|
|
16938
|
+
}
|
|
16939
|
+
__name(getRenderedTemplatePath, "getRenderedTemplatePath");
|
|
16940
|
+
function makeMetadata(options, safeProjectId) {
|
|
16941
|
+
return {
|
|
16942
|
+
projectId: options.projectId,
|
|
16943
|
+
safeProjectId,
|
|
16944
|
+
sessionId: options.sessionId,
|
|
16945
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
16946
|
+
cliVersion: VERSION,
|
|
16947
|
+
promptHash: sha256(options.agentPrompt),
|
|
16948
|
+
contextHash: sha256(options.agentContext),
|
|
16949
|
+
contextSchemaVersion: PROJECT_BUNDLE_SCHEMA_VERSION
|
|
16950
|
+
};
|
|
16951
|
+
}
|
|
16952
|
+
__name(makeMetadata, "makeMetadata");
|
|
16953
|
+
function getInstalledFramerSkillDirs(skillRoots) {
|
|
16954
|
+
return skillRoots.map((root) => path6.join(root, "framer")).filter((skillDir) => fs8.existsSync(path6.join(skillDir, "SKILL.md")));
|
|
16955
|
+
}
|
|
16956
|
+
__name(getInstalledFramerSkillDirs, "getInstalledFramerSkillDirs");
|
|
16957
|
+
function getProjectTemplateDir(skillDir) {
|
|
16958
|
+
const installedTemplateDir = path6.join(
|
|
16959
|
+
skillDir,
|
|
16960
|
+
"projects",
|
|
16961
|
+
PROJECT_TEMPLATE_ID
|
|
16962
|
+
);
|
|
16963
|
+
if (fs8.existsSync(installedTemplateDir)) return installedTemplateDir;
|
|
16964
|
+
return path6.join(
|
|
16965
|
+
__dirname2,
|
|
16966
|
+
"..",
|
|
16967
|
+
"skills",
|
|
16968
|
+
"framer",
|
|
16969
|
+
"projects",
|
|
16970
|
+
PROJECT_TEMPLATE_ID
|
|
16971
|
+
);
|
|
16972
|
+
}
|
|
16973
|
+
__name(getProjectTemplateDir, "getProjectTemplateDir");
|
|
16974
|
+
function cleanupStaleGeneratedProjectBundles(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
|
|
16975
|
+
const activeSafeProjectIds = new Set(activeProjectIds.map(toSafeProjectId));
|
|
16976
|
+
for (const skillDir of getInstalledFramerSkillDirs(skillRoots)) {
|
|
16977
|
+
const projectsDir = path6.join(skillDir, "projects");
|
|
16978
|
+
if (!fs8.existsSync(projectsDir)) continue;
|
|
16979
|
+
for (const entry of fs8.readdirSync(projectsDir, { withFileTypes: true })) {
|
|
16980
|
+
if (!entry.isDirectory()) continue;
|
|
16981
|
+
if (entry.name === PROJECT_TEMPLATE_ID) continue;
|
|
16982
|
+
if (activeSafeProjectIds.has(entry.name)) continue;
|
|
16983
|
+
fs8.rmSync(path6.join(projectsDir, entry.name), {
|
|
16984
|
+
recursive: true,
|
|
16985
|
+
force: true,
|
|
16986
|
+
maxRetries: 2
|
|
16987
|
+
});
|
|
16988
|
+
}
|
|
16989
|
+
}
|
|
16990
|
+
}
|
|
16991
|
+
__name(cleanupStaleGeneratedProjectBundles, "cleanupStaleGeneratedProjectBundles");
|
|
16992
|
+
function makeTemplateValues(metadata, sections, agentContext) {
|
|
16993
|
+
return {
|
|
16994
|
+
PROJECT_ID: metadata.projectId,
|
|
16995
|
+
SAFE_PROJECT_ID: metadata.safeProjectId,
|
|
16996
|
+
SESSION_ID: metadata.sessionId,
|
|
16997
|
+
GENERATED_AT: metadata.generatedAt,
|
|
16998
|
+
PROMPT_SECTION_LIST: sections.map((section) => `- \`prompt/${section.id}.md\` - ${section.title}`).join("\n"),
|
|
16999
|
+
PROJECT_CONTEXT: agentContext,
|
|
17000
|
+
READ_PROJECT_DOCS_COMMAND: replaceDefaultFramerAgentCommand(
|
|
17001
|
+
"npx @framer/agent@latest docs framer.agent.readProject"
|
|
17002
|
+
),
|
|
17003
|
+
READ_PROJECT_COMMAND: replaceDefaultFramerAgentCommand(
|
|
17004
|
+
`npx @framer/agent@latest read-project -s ${metadata.sessionId} -p "/" -q '<queries-json>'`
|
|
17005
|
+
)
|
|
17006
|
+
};
|
|
17007
|
+
}
|
|
17008
|
+
__name(makeTemplateValues, "makeTemplateValues");
|
|
17009
|
+
function writeGeneratedProjectBundle(options) {
|
|
17010
|
+
const skillRoots = options.skillRoots ?? getDefaultSkillRoots();
|
|
17011
|
+
const framerSkillDirs = getInstalledFramerSkillDirs(skillRoots);
|
|
17012
|
+
if (framerSkillDirs.length === 0) {
|
|
17013
|
+
throw new Error(
|
|
17014
|
+
"Could not find an installed framer skill. Run `@framer/agent setup` before `session new`."
|
|
17015
|
+
);
|
|
17016
|
+
}
|
|
17017
|
+
const safeProjectId = toSafeProjectId(options.projectId);
|
|
17018
|
+
if (safeProjectId === PROJECT_TEMPLATE_ID) {
|
|
17019
|
+
throw new Error(`Project ID resolves to reserved template directory.`);
|
|
17020
|
+
}
|
|
17021
|
+
const renderedPrompt = replaceDefaultFramerAgentCommand(options.agentPrompt);
|
|
17022
|
+
const renderedContext = replaceDefaultFramerAgentCommand(
|
|
17023
|
+
options.agentContext
|
|
17024
|
+
);
|
|
17025
|
+
const sections = splitPromptSections(renderedPrompt);
|
|
17026
|
+
const metadata = makeMetadata(
|
|
17027
|
+
{ ...options, agentPrompt: renderedPrompt, agentContext: renderedContext },
|
|
17028
|
+
safeProjectId
|
|
17029
|
+
);
|
|
17030
|
+
const paths = [];
|
|
17031
|
+
for (const skillDir of framerSkillDirs) {
|
|
17032
|
+
const projectDir = path6.join(skillDir, "projects", safeProjectId);
|
|
17033
|
+
const templateDir = getProjectTemplateDir(skillDir);
|
|
17034
|
+
fs8.rmSync(projectDir, { recursive: true, force: true, maxRetries: 2 });
|
|
17035
|
+
fs8.mkdirSync(projectDir, { recursive: true });
|
|
17036
|
+
renderFileTree(templateDir, projectDir, {
|
|
17037
|
+
templateValues: makeTemplateValues(metadata, sections, renderedContext),
|
|
17038
|
+
getRenderedDestinationPath: /* @__PURE__ */ __name(({ sourcePath, destinationPath }) => getRenderedTemplatePath(destinationPath) ?? (isTextFile(sourcePath) ? destinationPath : void 0), "getRenderedDestinationPath"),
|
|
17039
|
+
getSymlinkErrorMessage: /* @__PURE__ */ __name((sourcePath) => `Project template contains unsupported symlink: ${sourcePath}`, "getSymlinkErrorMessage"),
|
|
17040
|
+
transformRenderedContent: replaceDefaultFramerAgentCommand
|
|
17041
|
+
});
|
|
17042
|
+
for (const section of sections) {
|
|
17043
|
+
writeRenderedFile(
|
|
17044
|
+
path6.join(projectDir, "prompt", `${section.id}.md`),
|
|
17045
|
+
renderPromptFile(section)
|
|
17046
|
+
);
|
|
17047
|
+
}
|
|
17048
|
+
writeRenderedFile(
|
|
17049
|
+
path6.join(projectDir, "metadata.json"),
|
|
17050
|
+
JSON.stringify(metadata, null, " ")
|
|
17051
|
+
);
|
|
17052
|
+
paths.push(projectDir);
|
|
17053
|
+
}
|
|
17054
|
+
return { safeProjectId, paths };
|
|
17055
|
+
}
|
|
17056
|
+
__name(writeGeneratedProjectBundle, "writeGeneratedProjectBundle");
|
|
17057
|
+
|
|
16622
17058
|
// src/headless-server-url.ts
|
|
16623
17059
|
var PROD_HEADLESS_SERVER_URL = "wss://api.framer.com/channel/headless-plugin";
|
|
16624
17060
|
var DEV_HEADLESS_SERVER_URL = "wss://api.development.framer.com/channel/headless-plugin";
|
|
@@ -16670,7 +17106,7 @@ function inferHeadlessServerUrl(projectUrlOrId) {
|
|
|
16670
17106
|
}
|
|
16671
17107
|
__name(inferHeadlessServerUrl, "inferHeadlessServerUrl");
|
|
16672
17108
|
function getRelayTokenPath() {
|
|
16673
|
-
return
|
|
17109
|
+
return path6.join(getConfigDir(), "relay-token");
|
|
16674
17110
|
}
|
|
16675
17111
|
__name(getRelayTokenPath, "getRelayTokenPath");
|
|
16676
17112
|
function readRelayToken() {
|
|
@@ -16685,7 +17121,7 @@ __name(readRelayToken, "readRelayToken");
|
|
|
16685
17121
|
|
|
16686
17122
|
// src/relay-client.ts
|
|
16687
17123
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
16688
|
-
var
|
|
17124
|
+
var __dirname3 = path6.dirname(__filename$1);
|
|
16689
17125
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
16690
17126
|
var WAIT_TIMEOUT_SECONDS = 5;
|
|
16691
17127
|
var client = createTRPCClient({
|
|
@@ -16810,7 +17246,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16810
17246
|
logger?.log("Relay server not running, starting it...");
|
|
16811
17247
|
}
|
|
16812
17248
|
const isRunningFromSource = __filename$1.endsWith(".ts");
|
|
16813
|
-
const scriptPath = isRunningFromSource ?
|
|
17249
|
+
const scriptPath = isRunningFromSource ? path6.resolve(__dirname3, "./start-relay-server.ts") : path6.resolve(__dirname3, "./start-relay-server.js");
|
|
16814
17250
|
debug("relay", `spawning server process: ${scriptPath}`);
|
|
16815
17251
|
const serverProcess = spawn(
|
|
16816
17252
|
isRunningFromSource ? "tsx" : process.execPath,
|
|
@@ -16847,171 +17283,6 @@ async function waitForClaudeCodeSkillDiscovery() {
|
|
|
16847
17283
|
);
|
|
16848
17284
|
}
|
|
16849
17285
|
__name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
|
|
16850
|
-
var FRAMER_TEMPORARY_DIR = path7.join(os2.tmpdir(), "framer");
|
|
16851
|
-
function ensureTemporaryDir() {
|
|
16852
|
-
fs8.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
|
|
16853
|
-
}
|
|
16854
|
-
__name(ensureTemporaryDir, "ensureTemporaryDir");
|
|
16855
|
-
function cleanupStaleSessionCodeFiles(activeSessionIds) {
|
|
16856
|
-
if (!fs8.existsSync(FRAMER_TEMPORARY_DIR)) return;
|
|
16857
|
-
const activeSessionIdsSet = new Set(activeSessionIds);
|
|
16858
|
-
for (const entry of fs8.readdirSync(FRAMER_TEMPORARY_DIR)) {
|
|
16859
|
-
const [sessionId] = entry.split("-");
|
|
16860
|
-
if (activeSessionIdsSet.has(sessionId)) continue;
|
|
16861
|
-
const filePath = path7.join(FRAMER_TEMPORARY_DIR, entry);
|
|
16862
|
-
fs8.rmSync(filePath, { recursive: true, force: true, maxRetries: 2 });
|
|
16863
|
-
}
|
|
16864
|
-
}
|
|
16865
|
-
__name(cleanupStaleSessionCodeFiles, "cleanupStaleSessionCodeFiles");
|
|
16866
|
-
|
|
16867
|
-
// src/skills.ts
|
|
16868
|
-
var META_SKILL_NAME = "framer";
|
|
16869
|
-
var CODE_COMPONENTS_SKILL_NAME = "framer-code-components";
|
|
16870
|
-
var __dirname2 = path7.dirname(fileURLToPath(import.meta.url));
|
|
16871
|
-
var skillsDocsDir = path7.join(__dirname2, "..", "docs", "skills");
|
|
16872
|
-
function readSkillDoc(name2) {
|
|
16873
|
-
return fs8.readFileSync(path7.join(skillsDocsDir, name2), "utf-8").trimEnd();
|
|
16874
|
-
}
|
|
16875
|
-
__name(readSkillDoc, "readSkillDoc");
|
|
16876
|
-
function buildMetaSkill() {
|
|
16877
|
-
const command = getFramerAgentCommand();
|
|
16878
|
-
const template = readSkillDoc("framer.md");
|
|
16879
|
-
return `${renderTemplate(template, {
|
|
16880
|
-
FRAMER_TEMPORARY_DIR,
|
|
16881
|
-
FRAMER_AGENT_COMMAND: command
|
|
16882
|
-
})}
|
|
16883
|
-
`;
|
|
16884
|
-
}
|
|
16885
|
-
__name(buildMetaSkill, "buildMetaSkill");
|
|
16886
|
-
function buildCodeComponentsSkill() {
|
|
16887
|
-
return `${readSkillDoc("framer-code-components.md")}
|
|
16888
|
-
`;
|
|
16889
|
-
}
|
|
16890
|
-
__name(buildCodeComponentsSkill, "buildCodeComponentsSkill");
|
|
16891
|
-
function renderTemplate(template, values) {
|
|
16892
|
-
let rendered = template;
|
|
16893
|
-
for (const [key, value] of Object.entries(values)) {
|
|
16894
|
-
rendered = rendered.split(`{{${key}}}`).join(value);
|
|
16895
|
-
}
|
|
16896
|
-
return rendered;
|
|
16897
|
-
}
|
|
16898
|
-
__name(renderTemplate, "renderTemplate");
|
|
16899
|
-
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16900
|
-
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16901
|
-
var PACKAGE_NAME = "@framer/agent";
|
|
16902
|
-
var DEFAULT_FRAMER_AGENT_COMMAND = `npx ${PACKAGE_NAME}`;
|
|
16903
|
-
var RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND = `npx --min-release-age=0 ${PACKAGE_NAME}`;
|
|
16904
|
-
function getFramerAgentCommand() {
|
|
16905
|
-
return isNpxMinReleaseAgeOverrideEnabled() ? RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND : DEFAULT_FRAMER_AGENT_COMMAND;
|
|
16906
|
-
}
|
|
16907
|
-
__name(getFramerAgentCommand, "getFramerAgentCommand");
|
|
16908
|
-
function toSafeProjectId(projectId) {
|
|
16909
|
-
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16910
|
-
}
|
|
16911
|
-
__name(toSafeProjectId, "toSafeProjectId");
|
|
16912
|
-
function buildProjectSkill(projectId, agentContext, agentPrompt) {
|
|
16913
|
-
const command = getFramerAgentCommand();
|
|
16914
|
-
const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
|
|
16915
|
-
const template = readSkillDoc("framer-project.md");
|
|
16916
|
-
const content = `${renderTemplate(template, {
|
|
16917
|
-
SKILL_NAME: skillName,
|
|
16918
|
-
PROJECT_ID: projectId,
|
|
16919
|
-
GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16920
|
-
AGENT_PROMPT: agentPrompt.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16921
|
-
AGENT_CONTEXT: agentContext.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16922
|
-
FRAMER_TEMPORARY_DIR,
|
|
16923
|
-
FRAMER_AGENT_COMMAND: command
|
|
16924
|
-
})}
|
|
16925
|
-
`;
|
|
16926
|
-
return { skillName, content };
|
|
16927
|
-
}
|
|
16928
|
-
__name(buildProjectSkill, "buildProjectSkill");
|
|
16929
|
-
function writeSkill(root, skillName, content) {
|
|
16930
|
-
fs8.mkdirSync(root, { recursive: true });
|
|
16931
|
-
const rootStat = fs8.statSync(root);
|
|
16932
|
-
if (!rootStat.isDirectory()) {
|
|
16933
|
-
throw new Error(`Skill root is not a directory: ${root}`);
|
|
16934
|
-
}
|
|
16935
|
-
const skillDir = path7.join(root, skillName);
|
|
16936
|
-
const filePath = path7.join(skillDir, "SKILL.md");
|
|
16937
|
-
const current = fs8.lstatSync(skillDir, { throwIfNoEntry: false });
|
|
16938
|
-
if (current && (current.isSymbolicLink() || !current.isDirectory())) {
|
|
16939
|
-
fs8.rmSync(skillDir, { recursive: true, force: true });
|
|
16940
|
-
}
|
|
16941
|
-
fs8.mkdirSync(skillDir, { recursive: true });
|
|
16942
|
-
fs8.writeFileSync(filePath, content, "utf-8");
|
|
16943
|
-
return filePath;
|
|
16944
|
-
}
|
|
16945
|
-
__name(writeSkill, "writeSkill");
|
|
16946
|
-
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16947
|
-
for (const root of skillRoots) {
|
|
16948
|
-
if (!fs8.existsSync(root)) continue;
|
|
16949
|
-
for (const entry of fs8.readdirSync(root)) {
|
|
16950
|
-
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16951
|
-
const skillDir = path7.join(root, entry);
|
|
16952
|
-
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16953
|
-
}
|
|
16954
|
-
}
|
|
16955
|
-
}
|
|
16956
|
-
__name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
|
|
16957
|
-
function getDefaultSkillRoots() {
|
|
16958
|
-
const home = os2.homedir();
|
|
16959
|
-
return [
|
|
16960
|
-
path7.join(home, ".agents", "skills"),
|
|
16961
|
-
path7.join(home, ".claude", "skills")
|
|
16962
|
-
];
|
|
16963
|
-
}
|
|
16964
|
-
__name(getDefaultSkillRoots, "getDefaultSkillRoots");
|
|
16965
|
-
function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
|
|
16966
|
-
const sanitizedActiveProjectIds = new Set(
|
|
16967
|
-
[...activeProjectIds].map(toSafeProjectId)
|
|
16968
|
-
);
|
|
16969
|
-
for (const root of skillRoots) {
|
|
16970
|
-
if (!fs8.existsSync(root)) continue;
|
|
16971
|
-
for (const entry of fs8.readdirSync(root)) {
|
|
16972
|
-
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16973
|
-
const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
|
|
16974
|
-
const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
|
|
16975
|
-
if (isActive) continue;
|
|
16976
|
-
const skillDir = path7.join(root, entry);
|
|
16977
|
-
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16978
|
-
}
|
|
16979
|
-
}
|
|
16980
|
-
}
|
|
16981
|
-
__name(cleanupStaleSkills, "cleanupStaleSkills");
|
|
16982
|
-
function installSkills(options = { type: "base" }) {
|
|
16983
|
-
const skillRoots = options.skillRoots ?? getDefaultSkillRoots();
|
|
16984
|
-
const skills = [
|
|
16985
|
-
{ name: META_SKILL_NAME, content: buildMetaSkill() },
|
|
16986
|
-
{
|
|
16987
|
-
name: CODE_COMPONENTS_SKILL_NAME,
|
|
16988
|
-
content: buildCodeComponentsSkill()
|
|
16989
|
-
}
|
|
16990
|
-
];
|
|
16991
|
-
if (options.type === "project") {
|
|
16992
|
-
const projectSkill = buildProjectSkill(
|
|
16993
|
-
options.projectId,
|
|
16994
|
-
options.agentContext,
|
|
16995
|
-
options.agentPrompt
|
|
16996
|
-
);
|
|
16997
|
-
skills.push({
|
|
16998
|
-
name: projectSkill.skillName,
|
|
16999
|
-
content: projectSkill.content
|
|
17000
|
-
});
|
|
17001
|
-
}
|
|
17002
|
-
const results = skills.map((skill) => ({
|
|
17003
|
-
skillName: skill.name,
|
|
17004
|
-
paths: skillRoots.map(
|
|
17005
|
-
(root) => writeSkill(root, skill.name, skill.content)
|
|
17006
|
-
)
|
|
17007
|
-
}));
|
|
17008
|
-
trackSkillsInstall({
|
|
17009
|
-
skillType: options.type,
|
|
17010
|
-
skillCount: results.length
|
|
17011
|
-
});
|
|
17012
|
-
return results;
|
|
17013
|
-
}
|
|
17014
|
-
__name(installSkills, "installSkills");
|
|
17015
17286
|
|
|
17016
17287
|
// src/cli.ts
|
|
17017
17288
|
var PROGRAM_NAME = "@framer/agent";
|
|
@@ -17039,8 +17310,8 @@ function printSetupSummary(results) {
|
|
|
17039
17310
|
const installLocations = /* @__PURE__ */ new Set();
|
|
17040
17311
|
for (const result of results) {
|
|
17041
17312
|
for (const filePath of result.paths) {
|
|
17042
|
-
const skillDir =
|
|
17043
|
-
const root =
|
|
17313
|
+
const skillDir = path6.dirname(filePath);
|
|
17314
|
+
const root = path6.dirname(skillDir);
|
|
17044
17315
|
installLocations.add(root);
|
|
17045
17316
|
}
|
|
17046
17317
|
}
|
|
@@ -17068,6 +17339,13 @@ async function installBaseSkills() {
|
|
|
17068
17339
|
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17069
17340
|
);
|
|
17070
17341
|
}
|
|
17342
|
+
try {
|
|
17343
|
+
cleanupProjectSkills();
|
|
17344
|
+
} catch (error) {
|
|
17345
|
+
printWarning(
|
|
17346
|
+
`Failed to clean up generated project skills: ${formatErrorForUser(error)}`
|
|
17347
|
+
);
|
|
17348
|
+
}
|
|
17071
17349
|
const results = installSkills();
|
|
17072
17350
|
await waitForClaudeCodeSkillDiscovery();
|
|
17073
17351
|
return results;
|
|
@@ -17105,29 +17383,30 @@ async function getAgentContext(sessionId) {
|
|
|
17105
17383
|
return context;
|
|
17106
17384
|
}
|
|
17107
17385
|
__name(getAgentContext, "getAgentContext");
|
|
17108
|
-
async function
|
|
17386
|
+
async function refreshGeneratedProjectBundleFromSession(sessionId, projectId) {
|
|
17109
17387
|
try {
|
|
17110
17388
|
debug("skills", "fetching agent system prompt + context...");
|
|
17111
17389
|
const [agentPrompt, agentContext] = await Promise.all([
|
|
17112
17390
|
getAgentSystemPrompt(sessionId),
|
|
17113
17391
|
getAgentContext(sessionId)
|
|
17114
17392
|
]);
|
|
17115
|
-
debug("skills", "
|
|
17116
|
-
|
|
17117
|
-
type: "project",
|
|
17393
|
+
debug("skills", "writing generated project bundle...");
|
|
17394
|
+
writeGeneratedProjectBundle({
|
|
17118
17395
|
agentPrompt,
|
|
17119
17396
|
projectId,
|
|
17397
|
+
sessionId,
|
|
17120
17398
|
agentContext
|
|
17121
17399
|
});
|
|
17122
|
-
debug("skills", "
|
|
17400
|
+
debug("skills", "generated project bundle written");
|
|
17401
|
+
await waitForClaudeCodeSkillDiscovery();
|
|
17123
17402
|
} catch (err) {
|
|
17124
17403
|
throw errorWithRef(
|
|
17125
|
-
`Failed to refresh
|
|
17404
|
+
`Failed to refresh generated project bundle for session ${sessionId}: ${formatError(err)}`,
|
|
17126
17405
|
getErrorRef(err)
|
|
17127
17406
|
);
|
|
17128
17407
|
}
|
|
17129
17408
|
}
|
|
17130
|
-
__name(
|
|
17409
|
+
__name(refreshGeneratedProjectBundleFromSession, "refreshGeneratedProjectBundleFromSession");
|
|
17131
17410
|
async function resolveSessionCredentials(projectUrlOrId) {
|
|
17132
17411
|
const projectId = extractProjectId(projectUrlOrId);
|
|
17133
17412
|
try {
|
|
@@ -17316,13 +17595,18 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
17316
17595
|
});
|
|
17317
17596
|
sessionId = result.id;
|
|
17318
17597
|
debug("session.new", `session created id=${sessionId}`);
|
|
17319
|
-
debug(
|
|
17598
|
+
debug(
|
|
17599
|
+
"session.new",
|
|
17600
|
+
"fetching project name + refreshing generated bundle..."
|
|
17601
|
+
);
|
|
17320
17602
|
const [projectName] = await Promise.all([
|
|
17321
17603
|
getProjectName(sessionId),
|
|
17322
|
-
|
|
17604
|
+
refreshGeneratedProjectBundleFromSession(sessionId, projectId)
|
|
17323
17605
|
]);
|
|
17324
|
-
debug(
|
|
17325
|
-
|
|
17606
|
+
debug(
|
|
17607
|
+
"session.new",
|
|
17608
|
+
`project name="${projectName}", generated files written`
|
|
17609
|
+
);
|
|
17326
17610
|
saveProject({
|
|
17327
17611
|
projectId,
|
|
17328
17612
|
apiKey: credentials.apiKey,
|
|
@@ -17336,10 +17620,10 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
17336
17620
|
(activeSession) => activeSession.projectId
|
|
17337
17621
|
);
|
|
17338
17622
|
try {
|
|
17339
|
-
|
|
17623
|
+
cleanupStaleGeneratedProjectBundles(activeProjectIds);
|
|
17340
17624
|
} catch (error) {
|
|
17341
17625
|
printWarning(
|
|
17342
|
-
`Failed to clean up stale
|
|
17626
|
+
`Failed to clean up stale generated project bundles: ${formatErrorForUser(error)}`
|
|
17343
17627
|
);
|
|
17344
17628
|
}
|
|
17345
17629
|
const activeSessionIds = activeSessions.map(
|