@framer/agent 0.0.36 → 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 +479 -194
- 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
|
|
|
@@ -107,12 +107,12 @@ function openUrl(url) {
|
|
|
107
107
|
__name(openUrl, "openUrl");
|
|
108
108
|
function getConfigDir() {
|
|
109
109
|
if (process.env.XDG_CONFIG_HOME) {
|
|
110
|
-
return
|
|
110
|
+
return path6.join(process.env.XDG_CONFIG_HOME, "framer");
|
|
111
111
|
}
|
|
112
112
|
if (process.platform === "win32") {
|
|
113
|
-
return
|
|
113
|
+
return path6.join(process.env.APPDATA || os2.homedir(), "framer");
|
|
114
114
|
}
|
|
115
|
-
return
|
|
115
|
+
return path6.join(os2.homedir(), ".config", "framer");
|
|
116
116
|
}
|
|
117
117
|
__name(getConfigDir, "getConfigDir");
|
|
118
118
|
function ensureConfigDir() {
|
|
@@ -123,11 +123,11 @@ __name(ensureConfigDir, "ensureConfigDir");
|
|
|
123
123
|
|
|
124
124
|
// src/config/projects.ts
|
|
125
125
|
function getProjectsConfigPath() {
|
|
126
|
-
return
|
|
126
|
+
return path6.join(getConfigDir(), "projects.json");
|
|
127
127
|
}
|
|
128
128
|
__name(getProjectsConfigPath, "getProjectsConfigPath");
|
|
129
129
|
function getLegacyCredentialsPath() {
|
|
130
|
-
return
|
|
130
|
+
return path6.join(getConfigDir(), "credentials.json");
|
|
131
131
|
}
|
|
132
132
|
__name(getLegacyCredentialsPath, "getLegacyCredentialsPath");
|
|
133
133
|
var ProjectsConfigSchema = z.object({
|
|
@@ -313,7 +313,7 @@ var SettingsFileSchema = z.object({
|
|
|
313
313
|
telemetryNoticeShown: z.boolean().optional()
|
|
314
314
|
});
|
|
315
315
|
function getSettingsPath() {
|
|
316
|
-
return
|
|
316
|
+
return path6.join(getConfigDir(), "settings.json");
|
|
317
317
|
}
|
|
318
318
|
__name(getSettingsPath, "getSettingsPath");
|
|
319
319
|
var settings;
|
|
@@ -397,7 +397,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
397
397
|
// src/version.ts
|
|
398
398
|
var VERSION = (
|
|
399
399
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
400
|
-
"0.0.
|
|
400
|
+
"0.0.37"
|
|
401
401
|
);
|
|
402
402
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
403
403
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -1174,7 +1174,7 @@ var types = {
|
|
|
1174
1174
|
members: [
|
|
1175
1175
|
{
|
|
1176
1176
|
name: "type",
|
|
1177
|
-
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"',
|
|
1178
1178
|
description: "",
|
|
1179
1179
|
optional: false
|
|
1180
1180
|
},
|
|
@@ -7627,6 +7627,12 @@ var types = {
|
|
|
7627
7627
|
description: "@alpha",
|
|
7628
7628
|
optional: false
|
|
7629
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
|
+
},
|
|
7630
7636
|
{
|
|
7631
7637
|
name: "ping",
|
|
7632
7638
|
type: "() => Promise<void>",
|
|
@@ -7681,6 +7687,24 @@ var types = {
|
|
|
7681
7687
|
description: "@alpha",
|
|
7682
7688
|
optional: false
|
|
7683
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
|
+
},
|
|
7684
7708
|
{
|
|
7685
7709
|
name: "getScopeNodeForAgent",
|
|
7686
7710
|
type: "(input: {\n id: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
@@ -14248,6 +14272,20 @@ var methodsByCategory = {
|
|
|
14248
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.",
|
|
14249
14273
|
references: []
|
|
14250
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
|
+
},
|
|
14251
14289
|
{
|
|
14252
14290
|
name: "getGroundNode",
|
|
14253
14291
|
category: "FramerAgentAPI",
|
|
@@ -14283,6 +14321,13 @@ var methodsByCategory = {
|
|
|
14283
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.',
|
|
14284
14322
|
references: []
|
|
14285
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
|
+
},
|
|
14286
14331
|
{
|
|
14287
14332
|
name: "getScopeNode",
|
|
14288
14333
|
category: "FramerAgentAPI",
|
|
@@ -14395,6 +14440,13 @@ var methodsByCategory = {
|
|
|
14395
14440
|
description: "Renames a branch.\n\n@param branchId - Branch id to rename.\n@param title - New branch title.",
|
|
14396
14441
|
references: []
|
|
14397
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
|
+
},
|
|
14398
14450
|
{
|
|
14399
14451
|
name: "reviewChanges",
|
|
14400
14452
|
category: "FramerAgentAPI",
|
|
@@ -16618,6 +16670,391 @@ ${typeDef}`);
|
|
|
16618
16670
|
}
|
|
16619
16671
|
__name(renderDocs, "renderDocs");
|
|
16620
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
|
+
|
|
16621
17058
|
// src/headless-server-url.ts
|
|
16622
17059
|
var PROD_HEADLESS_SERVER_URL = "wss://api.framer.com/channel/headless-plugin";
|
|
16623
17060
|
var DEV_HEADLESS_SERVER_URL = "wss://api.development.framer.com/channel/headless-plugin";
|
|
@@ -16669,7 +17106,7 @@ function inferHeadlessServerUrl(projectUrlOrId) {
|
|
|
16669
17106
|
}
|
|
16670
17107
|
__name(inferHeadlessServerUrl, "inferHeadlessServerUrl");
|
|
16671
17108
|
function getRelayTokenPath() {
|
|
16672
|
-
return
|
|
17109
|
+
return path6.join(getConfigDir(), "relay-token");
|
|
16673
17110
|
}
|
|
16674
17111
|
__name(getRelayTokenPath, "getRelayTokenPath");
|
|
16675
17112
|
function readRelayToken() {
|
|
@@ -16684,7 +17121,7 @@ __name(readRelayToken, "readRelayToken");
|
|
|
16684
17121
|
|
|
16685
17122
|
// src/relay-client.ts
|
|
16686
17123
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
16687
|
-
var
|
|
17124
|
+
var __dirname3 = path6.dirname(__filename$1);
|
|
16688
17125
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
16689
17126
|
var WAIT_TIMEOUT_SECONDS = 5;
|
|
16690
17127
|
var client = createTRPCClient({
|
|
@@ -16809,7 +17246,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16809
17246
|
logger?.log("Relay server not running, starting it...");
|
|
16810
17247
|
}
|
|
16811
17248
|
const isRunningFromSource = __filename$1.endsWith(".ts");
|
|
16812
|
-
const scriptPath = isRunningFromSource ?
|
|
17249
|
+
const scriptPath = isRunningFromSource ? path6.resolve(__dirname3, "./start-relay-server.ts") : path6.resolve(__dirname3, "./start-relay-server.js");
|
|
16813
17250
|
debug("relay", `spawning server process: ${scriptPath}`);
|
|
16814
17251
|
const serverProcess = spawn(
|
|
16815
17252
|
isRunningFromSource ? "tsx" : process.execPath,
|
|
@@ -16846,171 +17283,6 @@ async function waitForClaudeCodeSkillDiscovery() {
|
|
|
16846
17283
|
);
|
|
16847
17284
|
}
|
|
16848
17285
|
__name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
|
|
16849
|
-
var FRAMER_TEMPORARY_DIR = path7.join(os2.tmpdir(), "framer");
|
|
16850
|
-
function ensureTemporaryDir() {
|
|
16851
|
-
fs8.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
|
|
16852
|
-
}
|
|
16853
|
-
__name(ensureTemporaryDir, "ensureTemporaryDir");
|
|
16854
|
-
function cleanupStaleSessionCodeFiles(activeSessionIds) {
|
|
16855
|
-
if (!fs8.existsSync(FRAMER_TEMPORARY_DIR)) return;
|
|
16856
|
-
const activeSessionIdsSet = new Set(activeSessionIds);
|
|
16857
|
-
for (const entry of fs8.readdirSync(FRAMER_TEMPORARY_DIR)) {
|
|
16858
|
-
const [sessionId] = entry.split("-");
|
|
16859
|
-
if (activeSessionIdsSet.has(sessionId)) continue;
|
|
16860
|
-
const filePath = path7.join(FRAMER_TEMPORARY_DIR, entry);
|
|
16861
|
-
fs8.rmSync(filePath, { recursive: true, force: true, maxRetries: 2 });
|
|
16862
|
-
}
|
|
16863
|
-
}
|
|
16864
|
-
__name(cleanupStaleSessionCodeFiles, "cleanupStaleSessionCodeFiles");
|
|
16865
|
-
|
|
16866
|
-
// src/skills.ts
|
|
16867
|
-
var META_SKILL_NAME = "framer";
|
|
16868
|
-
var CODE_COMPONENTS_SKILL_NAME = "framer-code-components";
|
|
16869
|
-
var __dirname2 = path7.dirname(fileURLToPath(import.meta.url));
|
|
16870
|
-
var skillsDocsDir = path7.join(__dirname2, "..", "docs", "skills");
|
|
16871
|
-
function readSkillDoc(name2) {
|
|
16872
|
-
return fs8.readFileSync(path7.join(skillsDocsDir, name2), "utf-8").trimEnd();
|
|
16873
|
-
}
|
|
16874
|
-
__name(readSkillDoc, "readSkillDoc");
|
|
16875
|
-
function buildMetaSkill() {
|
|
16876
|
-
const command = getFramerAgentCommand();
|
|
16877
|
-
const template = readSkillDoc("framer.md");
|
|
16878
|
-
return `${renderTemplate(template, {
|
|
16879
|
-
FRAMER_TEMPORARY_DIR,
|
|
16880
|
-
FRAMER_AGENT_COMMAND: command
|
|
16881
|
-
})}
|
|
16882
|
-
`;
|
|
16883
|
-
}
|
|
16884
|
-
__name(buildMetaSkill, "buildMetaSkill");
|
|
16885
|
-
function buildCodeComponentsSkill() {
|
|
16886
|
-
return `${readSkillDoc("framer-code-components.md")}
|
|
16887
|
-
`;
|
|
16888
|
-
}
|
|
16889
|
-
__name(buildCodeComponentsSkill, "buildCodeComponentsSkill");
|
|
16890
|
-
function renderTemplate(template, values) {
|
|
16891
|
-
let rendered = template;
|
|
16892
|
-
for (const [key, value] of Object.entries(values)) {
|
|
16893
|
-
rendered = rendered.split(`{{${key}}}`).join(value);
|
|
16894
|
-
}
|
|
16895
|
-
return rendered;
|
|
16896
|
-
}
|
|
16897
|
-
__name(renderTemplate, "renderTemplate");
|
|
16898
|
-
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16899
|
-
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16900
|
-
var PACKAGE_NAME = "@framer/agent";
|
|
16901
|
-
var DEFAULT_FRAMER_AGENT_COMMAND = `npx ${PACKAGE_NAME}`;
|
|
16902
|
-
var RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND = `npx --min-release-age=0 ${PACKAGE_NAME}`;
|
|
16903
|
-
function getFramerAgentCommand() {
|
|
16904
|
-
return isNpxMinReleaseAgeOverrideEnabled() ? RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND : DEFAULT_FRAMER_AGENT_COMMAND;
|
|
16905
|
-
}
|
|
16906
|
-
__name(getFramerAgentCommand, "getFramerAgentCommand");
|
|
16907
|
-
function toSafeProjectId(projectId) {
|
|
16908
|
-
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16909
|
-
}
|
|
16910
|
-
__name(toSafeProjectId, "toSafeProjectId");
|
|
16911
|
-
function buildProjectSkill(projectId, agentContext, agentPrompt) {
|
|
16912
|
-
const command = getFramerAgentCommand();
|
|
16913
|
-
const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
|
|
16914
|
-
const template = readSkillDoc("framer-project.md");
|
|
16915
|
-
const content = `${renderTemplate(template, {
|
|
16916
|
-
SKILL_NAME: skillName,
|
|
16917
|
-
PROJECT_ID: projectId,
|
|
16918
|
-
GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16919
|
-
AGENT_PROMPT: agentPrompt.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16920
|
-
AGENT_CONTEXT: agentContext.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16921
|
-
FRAMER_TEMPORARY_DIR,
|
|
16922
|
-
FRAMER_AGENT_COMMAND: command
|
|
16923
|
-
})}
|
|
16924
|
-
`;
|
|
16925
|
-
return { skillName, content };
|
|
16926
|
-
}
|
|
16927
|
-
__name(buildProjectSkill, "buildProjectSkill");
|
|
16928
|
-
function writeSkill(root, skillName, content) {
|
|
16929
|
-
fs8.mkdirSync(root, { recursive: true });
|
|
16930
|
-
const rootStat = fs8.statSync(root);
|
|
16931
|
-
if (!rootStat.isDirectory()) {
|
|
16932
|
-
throw new Error(`Skill root is not a directory: ${root}`);
|
|
16933
|
-
}
|
|
16934
|
-
const skillDir = path7.join(root, skillName);
|
|
16935
|
-
const filePath = path7.join(skillDir, "SKILL.md");
|
|
16936
|
-
const current = fs8.lstatSync(skillDir, { throwIfNoEntry: false });
|
|
16937
|
-
if (current && (current.isSymbolicLink() || !current.isDirectory())) {
|
|
16938
|
-
fs8.rmSync(skillDir, { recursive: true, force: true });
|
|
16939
|
-
}
|
|
16940
|
-
fs8.mkdirSync(skillDir, { recursive: true });
|
|
16941
|
-
fs8.writeFileSync(filePath, content, "utf-8");
|
|
16942
|
-
return filePath;
|
|
16943
|
-
}
|
|
16944
|
-
__name(writeSkill, "writeSkill");
|
|
16945
|
-
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16946
|
-
for (const root of skillRoots) {
|
|
16947
|
-
if (!fs8.existsSync(root)) continue;
|
|
16948
|
-
for (const entry of fs8.readdirSync(root)) {
|
|
16949
|
-
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16950
|
-
const skillDir = path7.join(root, entry);
|
|
16951
|
-
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16952
|
-
}
|
|
16953
|
-
}
|
|
16954
|
-
}
|
|
16955
|
-
__name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
|
|
16956
|
-
function getDefaultSkillRoots() {
|
|
16957
|
-
const home = os2.homedir();
|
|
16958
|
-
return [
|
|
16959
|
-
path7.join(home, ".agents", "skills"),
|
|
16960
|
-
path7.join(home, ".claude", "skills")
|
|
16961
|
-
];
|
|
16962
|
-
}
|
|
16963
|
-
__name(getDefaultSkillRoots, "getDefaultSkillRoots");
|
|
16964
|
-
function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
|
|
16965
|
-
const sanitizedActiveProjectIds = new Set(
|
|
16966
|
-
[...activeProjectIds].map(toSafeProjectId)
|
|
16967
|
-
);
|
|
16968
|
-
for (const root of skillRoots) {
|
|
16969
|
-
if (!fs8.existsSync(root)) continue;
|
|
16970
|
-
for (const entry of fs8.readdirSync(root)) {
|
|
16971
|
-
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16972
|
-
const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
|
|
16973
|
-
const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
|
|
16974
|
-
if (isActive) continue;
|
|
16975
|
-
const skillDir = path7.join(root, entry);
|
|
16976
|
-
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16977
|
-
}
|
|
16978
|
-
}
|
|
16979
|
-
}
|
|
16980
|
-
__name(cleanupStaleSkills, "cleanupStaleSkills");
|
|
16981
|
-
function installSkills(options = { type: "base" }) {
|
|
16982
|
-
const skillRoots = options.skillRoots ?? getDefaultSkillRoots();
|
|
16983
|
-
const skills = [
|
|
16984
|
-
{ name: META_SKILL_NAME, content: buildMetaSkill() },
|
|
16985
|
-
{
|
|
16986
|
-
name: CODE_COMPONENTS_SKILL_NAME,
|
|
16987
|
-
content: buildCodeComponentsSkill()
|
|
16988
|
-
}
|
|
16989
|
-
];
|
|
16990
|
-
if (options.type === "project") {
|
|
16991
|
-
const projectSkill = buildProjectSkill(
|
|
16992
|
-
options.projectId,
|
|
16993
|
-
options.agentContext,
|
|
16994
|
-
options.agentPrompt
|
|
16995
|
-
);
|
|
16996
|
-
skills.push({
|
|
16997
|
-
name: projectSkill.skillName,
|
|
16998
|
-
content: projectSkill.content
|
|
16999
|
-
});
|
|
17000
|
-
}
|
|
17001
|
-
const results = skills.map((skill) => ({
|
|
17002
|
-
skillName: skill.name,
|
|
17003
|
-
paths: skillRoots.map(
|
|
17004
|
-
(root) => writeSkill(root, skill.name, skill.content)
|
|
17005
|
-
)
|
|
17006
|
-
}));
|
|
17007
|
-
trackSkillsInstall({
|
|
17008
|
-
skillType: options.type,
|
|
17009
|
-
skillCount: results.length
|
|
17010
|
-
});
|
|
17011
|
-
return results;
|
|
17012
|
-
}
|
|
17013
|
-
__name(installSkills, "installSkills");
|
|
17014
17286
|
|
|
17015
17287
|
// src/cli.ts
|
|
17016
17288
|
var PROGRAM_NAME = "@framer/agent";
|
|
@@ -17038,8 +17310,8 @@ function printSetupSummary(results) {
|
|
|
17038
17310
|
const installLocations = /* @__PURE__ */ new Set();
|
|
17039
17311
|
for (const result of results) {
|
|
17040
17312
|
for (const filePath of result.paths) {
|
|
17041
|
-
const skillDir =
|
|
17042
|
-
const root =
|
|
17313
|
+
const skillDir = path6.dirname(filePath);
|
|
17314
|
+
const root = path6.dirname(skillDir);
|
|
17043
17315
|
installLocations.add(root);
|
|
17044
17316
|
}
|
|
17045
17317
|
}
|
|
@@ -17067,6 +17339,13 @@ async function installBaseSkills() {
|
|
|
17067
17339
|
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17068
17340
|
);
|
|
17069
17341
|
}
|
|
17342
|
+
try {
|
|
17343
|
+
cleanupProjectSkills();
|
|
17344
|
+
} catch (error) {
|
|
17345
|
+
printWarning(
|
|
17346
|
+
`Failed to clean up generated project skills: ${formatErrorForUser(error)}`
|
|
17347
|
+
);
|
|
17348
|
+
}
|
|
17070
17349
|
const results = installSkills();
|
|
17071
17350
|
await waitForClaudeCodeSkillDiscovery();
|
|
17072
17351
|
return results;
|
|
@@ -17104,29 +17383,30 @@ async function getAgentContext(sessionId) {
|
|
|
17104
17383
|
return context;
|
|
17105
17384
|
}
|
|
17106
17385
|
__name(getAgentContext, "getAgentContext");
|
|
17107
|
-
async function
|
|
17386
|
+
async function refreshGeneratedProjectBundleFromSession(sessionId, projectId) {
|
|
17108
17387
|
try {
|
|
17109
17388
|
debug("skills", "fetching agent system prompt + context...");
|
|
17110
17389
|
const [agentPrompt, agentContext] = await Promise.all([
|
|
17111
17390
|
getAgentSystemPrompt(sessionId),
|
|
17112
17391
|
getAgentContext(sessionId)
|
|
17113
17392
|
]);
|
|
17114
|
-
debug("skills", "
|
|
17115
|
-
|
|
17116
|
-
type: "project",
|
|
17393
|
+
debug("skills", "writing generated project bundle...");
|
|
17394
|
+
writeGeneratedProjectBundle({
|
|
17117
17395
|
agentPrompt,
|
|
17118
17396
|
projectId,
|
|
17397
|
+
sessionId,
|
|
17119
17398
|
agentContext
|
|
17120
17399
|
});
|
|
17121
|
-
debug("skills", "
|
|
17400
|
+
debug("skills", "generated project bundle written");
|
|
17401
|
+
await waitForClaudeCodeSkillDiscovery();
|
|
17122
17402
|
} catch (err) {
|
|
17123
17403
|
throw errorWithRef(
|
|
17124
|
-
`Failed to refresh
|
|
17404
|
+
`Failed to refresh generated project bundle for session ${sessionId}: ${formatError(err)}`,
|
|
17125
17405
|
getErrorRef(err)
|
|
17126
17406
|
);
|
|
17127
17407
|
}
|
|
17128
17408
|
}
|
|
17129
|
-
__name(
|
|
17409
|
+
__name(refreshGeneratedProjectBundleFromSession, "refreshGeneratedProjectBundleFromSession");
|
|
17130
17410
|
async function resolveSessionCredentials(projectUrlOrId) {
|
|
17131
17411
|
const projectId = extractProjectId(projectUrlOrId);
|
|
17132
17412
|
try {
|
|
@@ -17315,13 +17595,18 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
17315
17595
|
});
|
|
17316
17596
|
sessionId = result.id;
|
|
17317
17597
|
debug("session.new", `session created id=${sessionId}`);
|
|
17318
|
-
debug(
|
|
17598
|
+
debug(
|
|
17599
|
+
"session.new",
|
|
17600
|
+
"fetching project name + refreshing generated bundle..."
|
|
17601
|
+
);
|
|
17319
17602
|
const [projectName] = await Promise.all([
|
|
17320
17603
|
getProjectName(sessionId),
|
|
17321
|
-
|
|
17604
|
+
refreshGeneratedProjectBundleFromSession(sessionId, projectId)
|
|
17322
17605
|
]);
|
|
17323
|
-
debug(
|
|
17324
|
-
|
|
17606
|
+
debug(
|
|
17607
|
+
"session.new",
|
|
17608
|
+
`project name="${projectName}", generated files written`
|
|
17609
|
+
);
|
|
17325
17610
|
saveProject({
|
|
17326
17611
|
projectId,
|
|
17327
17612
|
apiKey: credentials.apiKey,
|
|
@@ -17335,10 +17620,10 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
17335
17620
|
(activeSession) => activeSession.projectId
|
|
17336
17621
|
);
|
|
17337
17622
|
try {
|
|
17338
|
-
|
|
17623
|
+
cleanupStaleGeneratedProjectBundles(activeProjectIds);
|
|
17339
17624
|
} catch (error) {
|
|
17340
17625
|
printWarning(
|
|
17341
|
-
`Failed to clean up stale
|
|
17626
|
+
`Failed to clean up stale generated project bundles: ${formatErrorForUser(error)}`
|
|
17342
17627
|
);
|
|
17343
17628
|
}
|
|
17344
17629
|
const activeSessionIds = activeSessions.map(
|