@getcatalystiq/agent-plane-ui 0.1.5 → 0.1.7
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/index.cjs +451 -120
- package/dist/index.js +451 -120
- package/package.json +1 -1
- package/dist/charts.d.cts +0 -13
- package/dist/charts.d.ts +0 -13
- package/dist/editor.d.cts +0 -2
- package/dist/editor.d.ts +0 -2
- package/dist/index.d.cts +0 -636
- package/dist/index.d.ts +0 -636
package/dist/index.cjs
CHANGED
|
@@ -1091,6 +1091,7 @@ function CancelRunButton({ runId, onCancelled }) {
|
|
|
1091
1091
|
] }) })
|
|
1092
1092
|
] });
|
|
1093
1093
|
}
|
|
1094
|
+
var emptyForm = { name: "", slug: "", description: "", base_url: "", mcp_endpoint_path: "/mcp" };
|
|
1094
1095
|
function McpServerListPage({ initialData }) {
|
|
1095
1096
|
const { mutate } = useSWR.useSWRConfig();
|
|
1096
1097
|
const client = useAgentPlaneClient();
|
|
@@ -1099,21 +1100,48 @@ function McpServerListPage({ initialData }) {
|
|
|
1099
1100
|
(c) => c.customConnectors.listServers(),
|
|
1100
1101
|
initialData ? { fallbackData: initialData } : void 0
|
|
1101
1102
|
);
|
|
1102
|
-
const [
|
|
1103
|
-
const [
|
|
1104
|
-
const [
|
|
1103
|
+
const [showCreate, setShowCreate] = React3.useState(false);
|
|
1104
|
+
const [creating, setCreating] = React3.useState(false);
|
|
1105
|
+
const [createForm, setCreateForm] = React3.useState(emptyForm);
|
|
1106
|
+
const [createError, setCreateError] = React3.useState("");
|
|
1107
|
+
const [editTarget, setEditTarget] = React3.useState(null);
|
|
1108
|
+
const [editing, setEditing] = React3.useState(false);
|
|
1109
|
+
const [editForm, setEditForm] = React3.useState({ name: "", description: "" });
|
|
1110
|
+
const [editError, setEditError] = React3.useState("");
|
|
1105
1111
|
const [deleteTarget, setDeleteTarget] = React3.useState(null);
|
|
1106
1112
|
const [deleting, setDeleting] = React3.useState(false);
|
|
1107
1113
|
const [deleteError, setDeleteError] = React3.useState("");
|
|
1108
|
-
async function
|
|
1109
|
-
|
|
1114
|
+
async function handleCreate() {
|
|
1115
|
+
setCreating(true);
|
|
1116
|
+
setCreateError("");
|
|
1110
1117
|
try {
|
|
1111
|
-
await client.customConnectors.createServer(
|
|
1112
|
-
|
|
1113
|
-
|
|
1118
|
+
await client.customConnectors.createServer(createForm);
|
|
1119
|
+
setShowCreate(false);
|
|
1120
|
+
setCreateForm(emptyForm);
|
|
1114
1121
|
mutate("mcp-servers");
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
setCreateError(err instanceof Error ? err.message : "Failed to create");
|
|
1115
1124
|
} finally {
|
|
1116
|
-
|
|
1125
|
+
setCreating(false);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function openEdit(server) {
|
|
1129
|
+
setEditTarget(server);
|
|
1130
|
+
setEditForm({ name: server.name, description: server.description });
|
|
1131
|
+
setEditError("");
|
|
1132
|
+
}
|
|
1133
|
+
async function handleEdit() {
|
|
1134
|
+
if (!editTarget) return;
|
|
1135
|
+
setEditing(true);
|
|
1136
|
+
setEditError("");
|
|
1137
|
+
try {
|
|
1138
|
+
await client.customConnectors.updateServer(editTarget.id, editForm);
|
|
1139
|
+
setEditTarget(null);
|
|
1140
|
+
mutate("mcp-servers");
|
|
1141
|
+
} catch (err) {
|
|
1142
|
+
setEditError(err instanceof Error ? err.message : "Failed to update");
|
|
1143
|
+
} finally {
|
|
1144
|
+
setEditing(false);
|
|
1117
1145
|
}
|
|
1118
1146
|
}
|
|
1119
1147
|
async function handleDelete() {
|
|
@@ -1132,7 +1160,7 @@ function McpServerListPage({ initialData }) {
|
|
|
1132
1160
|
}
|
|
1133
1161
|
if (error) {
|
|
1134
1162
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-destructive", children: [
|
|
1135
|
-
"Failed to load
|
|
1163
|
+
"Failed to load connectors: ",
|
|
1136
1164
|
error.message
|
|
1137
1165
|
] }) });
|
|
1138
1166
|
}
|
|
@@ -1140,17 +1168,7 @@ function McpServerListPage({ initialData }) {
|
|
|
1140
1168
|
return /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-96 rounded-lg" });
|
|
1141
1169
|
}
|
|
1142
1170
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
|
|
1143
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: () =>
|
|
1144
|
-
showAdd && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border p-4 space-y-3", children: [
|
|
1145
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
1146
|
-
/* @__PURE__ */ jsxRuntime.jsx(Input, { placeholder: "Name", value: newServer.name, onChange: (e) => setNewServer({ ...newServer, name: e.target.value }) }),
|
|
1147
|
-
/* @__PURE__ */ jsxRuntime.jsx(Input, { placeholder: "Slug", value: newServer.slug, onChange: (e) => setNewServer({ ...newServer, slug: e.target.value }) }),
|
|
1148
|
-
/* @__PURE__ */ jsxRuntime.jsx(Input, { placeholder: "Description", value: newServer.description, onChange: (e) => setNewServer({ ...newServer, description: e.target.value }) }),
|
|
1149
|
-
/* @__PURE__ */ jsxRuntime.jsx(Input, { placeholder: "Base URL", value: newServer.base_url, onChange: (e) => setNewServer({ ...newServer, base_url: e.target.value }) }),
|
|
1150
|
-
/* @__PURE__ */ jsxRuntime.jsx(Input, { placeholder: "MCP Endpoint Path", value: newServer.mcp_endpoint_path, onChange: (e) => setNewServer({ ...newServer, mcp_endpoint_path: e.target.value }) })
|
|
1151
|
-
] }),
|
|
1152
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", onClick: handleAdd, disabled: adding || !newServer.name || !newServer.base_url, children: adding ? "Adding..." : "Add Server" })
|
|
1153
|
-
] }),
|
|
1171
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowCreate(true), children: "+ New Connector" }) }),
|
|
1154
1172
|
/* @__PURE__ */ jsxRuntime.jsxs(AdminTable, { children: [
|
|
1155
1173
|
/* @__PURE__ */ jsxRuntime.jsxs(AdminTableHead, { children: [
|
|
1156
1174
|
/* @__PURE__ */ jsxRuntime.jsx(Th, { children: "Name" }),
|
|
@@ -1164,10 +1182,17 @@ function McpServerListPage({ initialData }) {
|
|
|
1164
1182
|
] }),
|
|
1165
1183
|
/* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
|
|
1166
1184
|
servers.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(AdminTableRow, { children: [
|
|
1167
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1185
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1186
|
+
"button",
|
|
1187
|
+
{
|
|
1188
|
+
onClick: () => openEdit(s),
|
|
1189
|
+
className: "flex items-center gap-2 text-left hover:underline cursor-pointer",
|
|
1190
|
+
children: [
|
|
1191
|
+
s.logo_url && /* @__PURE__ */ jsxRuntime.jsx("img", { src: s.logo_url, alt: "", className: "w-5 h-5 rounded-sm object-contain" }),
|
|
1192
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-primary", children: s.name })
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
) }),
|
|
1171
1196
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: s.slug }),
|
|
1172
1197
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground truncate max-w-xs", title: s.base_url, children: s.base_url }),
|
|
1173
1198
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: s.client_id ? "default" : "secondary", children: s.client_id ? "Registered" : "No DCR" }) }),
|
|
@@ -1185,9 +1210,55 @@ function McpServerListPage({ initialData }) {
|
|
|
1185
1210
|
}
|
|
1186
1211
|
) })
|
|
1187
1212
|
] }, s.id)),
|
|
1188
|
-
servers.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(EmptyRow, { colSpan: 8, children: 'No custom connectors registered. Click "
|
|
1213
|
+
servers.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(EmptyRow, { colSpan: 8, children: 'No custom connectors registered. Click "+ New Connector" to add one.' })
|
|
1189
1214
|
] })
|
|
1190
1215
|
] }),
|
|
1216
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: showCreate, onOpenChange: setShowCreate, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { children: [
|
|
1217
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Register Connector" }) }),
|
|
1218
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogBody, { className: "space-y-3", children: [
|
|
1219
|
+
createError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: createError }),
|
|
1220
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: createForm.name, onChange: (e) => setCreateForm({ ...createForm, name: e.target.value }), placeholder: "My MCP Server" }) }),
|
|
1221
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Slug", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: createForm.slug, onChange: (e) => setCreateForm({ ...createForm, slug: e.target.value }), placeholder: "my-mcp-server" }) }),
|
|
1222
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: createForm.description, onChange: (e) => setCreateForm({ ...createForm, description: e.target.value }), placeholder: "What this connector does" }) }),
|
|
1223
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Base URL", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: createForm.base_url, onChange: (e) => setCreateForm({ ...createForm, base_url: e.target.value }), placeholder: "https://my-server.example.com" }) }),
|
|
1224
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "MCP Endpoint Path", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: createForm.mcp_endpoint_path, onChange: (e) => setCreateForm({ ...createForm, mcp_endpoint_path: e.target.value }), placeholder: "/mcp" }) })
|
|
1225
|
+
] }),
|
|
1226
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
|
|
1227
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => setShowCreate(false), children: "Cancel" }),
|
|
1228
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleCreate, disabled: creating || !createForm.name || !createForm.base_url, children: creating ? "Creating..." : "Create" })
|
|
1229
|
+
] })
|
|
1230
|
+
] }) }),
|
|
1231
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: !!editTarget, onOpenChange: (open) => {
|
|
1232
|
+
if (!open) setEditTarget(null);
|
|
1233
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { children: [
|
|
1234
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Edit Connector" }) }),
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogBody, { className: "space-y-3", children: [
|
|
1236
|
+
editError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: editError }),
|
|
1237
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: editForm.name, onChange: (e) => setEditForm({ ...editForm, name: e.target.value }) }) }),
|
|
1238
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsxRuntime.jsx(Input, { value: editForm.description, onChange: (e) => setEditForm({ ...editForm, description: e.target.value }) }) }),
|
|
1239
|
+
editTarget && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [
|
|
1240
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1241
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Slug:" }),
|
|
1242
|
+
" ",
|
|
1243
|
+
editTarget.slug
|
|
1244
|
+
] }),
|
|
1245
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1246
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Base URL:" }),
|
|
1247
|
+
" ",
|
|
1248
|
+
editTarget.base_url
|
|
1249
|
+
] }),
|
|
1250
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1251
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Endpoint:" }),
|
|
1252
|
+
" ",
|
|
1253
|
+
editTarget.mcp_endpoint_path
|
|
1254
|
+
] })
|
|
1255
|
+
] })
|
|
1256
|
+
] }),
|
|
1257
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => setEditTarget(null), children: "Cancel" }),
|
|
1259
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleEdit, disabled: editing || !editForm.name, children: editing ? "Saving..." : "Save" })
|
|
1260
|
+
] })
|
|
1261
|
+
] }) }),
|
|
1191
1262
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1192
1263
|
ConfirmDialog,
|
|
1193
1264
|
{
|
|
@@ -1198,14 +1269,14 @@ function McpServerListPage({ initialData }) {
|
|
|
1198
1269
|
setDeleteError("");
|
|
1199
1270
|
}
|
|
1200
1271
|
},
|
|
1201
|
-
title: "Delete
|
|
1272
|
+
title: "Delete Connector",
|
|
1202
1273
|
confirmLabel: "Delete",
|
|
1203
1274
|
loadingLabel: "Deleting...",
|
|
1204
1275
|
loading: deleting,
|
|
1205
1276
|
error: deleteError,
|
|
1206
1277
|
onConfirm: handleDelete,
|
|
1207
1278
|
children: [
|
|
1208
|
-
"Delete
|
|
1279
|
+
"Delete connector ",
|
|
1209
1280
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: deleteTarget?.name }),
|
|
1210
1281
|
"? This cannot be undone."
|
|
1211
1282
|
]
|
|
@@ -3100,128 +3171,388 @@ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAl
|
|
|
3100
3171
|
] });
|
|
3101
3172
|
}
|
|
3102
3173
|
var CodeEditor = React3.lazy(() => import('./code-editor-ZD5ZILTM.cjs'));
|
|
3103
|
-
function
|
|
3104
|
-
const
|
|
3105
|
-
const
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3174
|
+
function buildTree(files) {
|
|
3175
|
+
const rootFiles = [];
|
|
3176
|
+
const dirMap = /* @__PURE__ */ new Map();
|
|
3177
|
+
function ensureDir(dirPath) {
|
|
3178
|
+
const existing = dirMap.get(dirPath);
|
|
3179
|
+
if (existing) return existing;
|
|
3180
|
+
const parts = dirPath.split("/");
|
|
3181
|
+
const node = {
|
|
3182
|
+
name: parts[parts.length - 1],
|
|
3183
|
+
fullPath: dirPath,
|
|
3184
|
+
children: [],
|
|
3185
|
+
files: []
|
|
3186
|
+
};
|
|
3187
|
+
dirMap.set(dirPath, node);
|
|
3188
|
+
if (parts.length > 1) {
|
|
3189
|
+
const parentPath = parts.slice(0, -1).join("/");
|
|
3190
|
+
const parent = ensureDir(parentPath);
|
|
3191
|
+
if (!parent.children.some((c) => c.fullPath === dirPath)) {
|
|
3192
|
+
parent.children.push(node);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
return node;
|
|
3196
|
+
}
|
|
3197
|
+
for (const file of files) {
|
|
3198
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3199
|
+
if (slashIdx === -1) {
|
|
3200
|
+
rootFiles.push(file);
|
|
3201
|
+
} else {
|
|
3202
|
+
const dirPath = file.path.slice(0, slashIdx);
|
|
3203
|
+
const dir = ensureDir(dirPath);
|
|
3204
|
+
dir.files.push(file);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
const topLevel = [];
|
|
3208
|
+
for (const node of dirMap.values()) {
|
|
3209
|
+
if (!node.fullPath.includes("/")) {
|
|
3210
|
+
topLevel.push(node);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
function sortNode(node) {
|
|
3214
|
+
node.children.sort((a, b) => a.name.localeCompare(b.name));
|
|
3215
|
+
node.files.sort((a, b) => a.path.localeCompare(b.path));
|
|
3216
|
+
node.children.forEach(sortNode);
|
|
3217
|
+
}
|
|
3218
|
+
topLevel.forEach(sortNode);
|
|
3219
|
+
topLevel.sort((a, b) => a.name.localeCompare(b.name));
|
|
3220
|
+
rootFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
3221
|
+
return { rootFiles, rootDirs: topLevel };
|
|
3222
|
+
}
|
|
3223
|
+
function collectAllDirPaths(nodes) {
|
|
3224
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3225
|
+
function walk(node) {
|
|
3226
|
+
paths.add(node.fullPath);
|
|
3227
|
+
node.children.forEach(walk);
|
|
3228
|
+
}
|
|
3229
|
+
nodes.forEach(walk);
|
|
3230
|
+
return paths;
|
|
3231
|
+
}
|
|
3232
|
+
function FileTreeEditor({
|
|
3233
|
+
initialFiles,
|
|
3234
|
+
onSave,
|
|
3235
|
+
onChange,
|
|
3236
|
+
readOnly = false,
|
|
3237
|
+
hideSave = false,
|
|
3238
|
+
title = "Files",
|
|
3239
|
+
saveLabel = "Save",
|
|
3240
|
+
addFolderLabel = "Folder",
|
|
3241
|
+
newFileTemplate = { filename: "SKILL.md", content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n" },
|
|
3242
|
+
savedVersion
|
|
3243
|
+
}) {
|
|
3114
3244
|
const [files, setFiles] = React3.useState(initialFiles);
|
|
3115
|
-
const [selectedPath, setSelectedPath] = React3.useState(
|
|
3245
|
+
const [selectedPath, setSelectedPath] = React3.useState(
|
|
3246
|
+
initialFiles.length > 0 ? initialFiles[0].path : null
|
|
3247
|
+
);
|
|
3116
3248
|
const [saving, setSaving] = React3.useState(false);
|
|
3117
|
-
const [
|
|
3249
|
+
const [expanded, setExpanded] = React3.useState(() => {
|
|
3250
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3251
|
+
return collectAllDirPaths(rootDirs);
|
|
3252
|
+
});
|
|
3253
|
+
const [showAddFolder, setShowAddFolder] = React3.useState(false);
|
|
3254
|
+
const [newFolderName, setNewFolderName] = React3.useState("");
|
|
3255
|
+
const [addingFileInDir, setAddingFileInDir] = React3.useState(null);
|
|
3118
3256
|
const [newFileName, setNewFileName] = React3.useState("");
|
|
3257
|
+
const [savedSnapshot, setSavedSnapshot] = React3.useState(() => JSON.stringify(initialFiles));
|
|
3258
|
+
React3.useEffect(() => {
|
|
3259
|
+
const snap = JSON.stringify(initialFiles);
|
|
3260
|
+
setSavedSnapshot(snap);
|
|
3261
|
+
setFiles(initialFiles);
|
|
3262
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3263
|
+
setExpanded(collectAllDirPaths(rootDirs));
|
|
3264
|
+
}, [initialFiles]);
|
|
3265
|
+
React3.useEffect(() => {
|
|
3266
|
+
if (savedVersion !== void 0 && savedVersion > 0) {
|
|
3267
|
+
setSavedSnapshot(JSON.stringify(files));
|
|
3268
|
+
}
|
|
3269
|
+
}, [savedVersion]);
|
|
3270
|
+
const onChangeRef = React3.useRef(onChange);
|
|
3271
|
+
onChangeRef.current = onChange;
|
|
3272
|
+
React3.useEffect(() => {
|
|
3273
|
+
if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
|
|
3274
|
+
onChangeRef.current(files);
|
|
3275
|
+
}
|
|
3276
|
+
}, [files, savedSnapshot]);
|
|
3119
3277
|
const isDirty = React3.useMemo(
|
|
3120
|
-
() => JSON.stringify(files) !==
|
|
3121
|
-
[files,
|
|
3278
|
+
() => JSON.stringify(files) !== savedSnapshot,
|
|
3279
|
+
[files, savedSnapshot]
|
|
3280
|
+
);
|
|
3281
|
+
const tree = React3.useMemo(() => buildTree(files), [files]);
|
|
3282
|
+
const activeFile = React3.useMemo(
|
|
3283
|
+
() => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
|
|
3284
|
+
[files, selectedPath]
|
|
3122
3285
|
);
|
|
3123
|
-
const
|
|
3124
|
-
|
|
3125
|
-
setFiles((prev) => prev.map((f) => f.path ===
|
|
3286
|
+
const handleEditorChange = React3.useCallback((value) => {
|
|
3287
|
+
if (readOnly || !selectedPath) return;
|
|
3288
|
+
setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
|
|
3289
|
+
}, [readOnly, selectedPath]);
|
|
3290
|
+
function toggleExpand(dirPath) {
|
|
3291
|
+
setExpanded((prev) => {
|
|
3292
|
+
const next = new Set(prev);
|
|
3293
|
+
if (next.has(dirPath)) next.delete(dirPath);
|
|
3294
|
+
else next.add(dirPath);
|
|
3295
|
+
return next;
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
function addFolder() {
|
|
3299
|
+
const name = newFolderName.trim();
|
|
3300
|
+
if (!name) return;
|
|
3301
|
+
const filePath = `${name}/${newFileTemplate.filename}`;
|
|
3302
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3303
|
+
const content = newFileTemplate.content.replace("# New", `# ${name}`);
|
|
3304
|
+
setFiles((prev) => [...prev, { path: filePath, content }]);
|
|
3305
|
+
setSelectedPath(filePath);
|
|
3306
|
+
setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
|
|
3307
|
+
setNewFolderName("");
|
|
3308
|
+
setShowAddFolder(false);
|
|
3126
3309
|
}
|
|
3127
|
-
function
|
|
3310
|
+
function removeDir(dirPath) {
|
|
3311
|
+
const prefix = dirPath + "/";
|
|
3312
|
+
const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
|
|
3313
|
+
if (affectedFiles.length === 0) return;
|
|
3314
|
+
if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
|
|
3315
|
+
setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
|
|
3316
|
+
if (selectedPath && selectedPath.startsWith(prefix)) setSelectedPath(null);
|
|
3317
|
+
}
|
|
3318
|
+
function removeFile(filePath) {
|
|
3319
|
+
const fileName = filePath.split("/").pop() ?? filePath;
|
|
3320
|
+
if (!confirm(`Remove file "${fileName}"?`)) return;
|
|
3321
|
+
setFiles((prev) => prev.filter((f) => f.path !== filePath));
|
|
3322
|
+
if (selectedPath === filePath) setSelectedPath(null);
|
|
3323
|
+
}
|
|
3324
|
+
function addFileInDir(dirPath) {
|
|
3128
3325
|
const name = newFileName.trim();
|
|
3129
|
-
if (!name
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3326
|
+
if (!name) return;
|
|
3327
|
+
const filePath = dirPath ? `${dirPath}/${name}` : name;
|
|
3328
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3329
|
+
setFiles((prev) => [...prev, { path: filePath, content: "" }]);
|
|
3330
|
+
setSelectedPath(filePath);
|
|
3134
3331
|
setNewFileName("");
|
|
3332
|
+
setAddingFileInDir(null);
|
|
3135
3333
|
}
|
|
3136
|
-
function
|
|
3137
|
-
setFiles((prev) => prev.filter((f) => f.path !== path));
|
|
3138
|
-
if (selectedPath === path) {
|
|
3139
|
-
setSelectedPath(files.find((f) => f.path !== path)?.path ?? null);
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
const handleSave = React3.useCallback(async () => {
|
|
3334
|
+
async function handleSave() {
|
|
3143
3335
|
setSaving(true);
|
|
3144
3336
|
try {
|
|
3145
|
-
|
|
3146
|
-
for (const file of files) {
|
|
3147
|
-
const slashIdx = file.path.lastIndexOf("/");
|
|
3148
|
-
if (slashIdx === -1) {
|
|
3149
|
-
const existing = folderMap.get("(root)") ?? [];
|
|
3150
|
-
existing.push({ path: file.path, content: file.content });
|
|
3151
|
-
folderMap.set("(root)", existing);
|
|
3152
|
-
} else {
|
|
3153
|
-
const folder = file.path.slice(0, slashIdx);
|
|
3154
|
-
const fileName = file.path.slice(slashIdx + 1);
|
|
3155
|
-
const existing = folderMap.get(folder) ?? [];
|
|
3156
|
-
existing.push({ path: fileName, content: file.content });
|
|
3157
|
-
folderMap.set(folder, existing);
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3161
|
-
await client.agents.update(agentId, { skills });
|
|
3162
|
-
onSaved?.();
|
|
3337
|
+
await onSave(files);
|
|
3163
3338
|
} finally {
|
|
3164
3339
|
setSaving(false);
|
|
3165
3340
|
}
|
|
3166
|
-
}
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3171
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || !isDirty, children: saving ? "Saving..." : "Save Skills" })
|
|
3172
|
-
] }) }),
|
|
3173
|
-
addingFile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
|
|
3174
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3175
|
-
Input,
|
|
3176
|
-
{
|
|
3177
|
-
value: newFileName,
|
|
3178
|
-
onChange: (e) => setNewFileName(e.target.value),
|
|
3179
|
-
placeholder: "folder/SKILL.md",
|
|
3180
|
-
className: "max-w-xs text-sm",
|
|
3181
|
-
onKeyDown: (e) => e.key === "Enter" && addFile()
|
|
3182
|
-
}
|
|
3183
|
-
),
|
|
3184
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", onClick: addFile, children: "Add" }),
|
|
3185
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
|
|
3186
|
-
setAddingFile(false);
|
|
3187
|
-
setNewFileName("");
|
|
3188
|
-
}, children: "Cancel" })
|
|
3189
|
-
] }),
|
|
3190
|
-
files.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: 'No skills defined. Click "Add File" to create a skill.' }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[300px]", children: [
|
|
3191
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-48 shrink-0 border-r border-border pr-3 space-y-1", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3341
|
+
}
|
|
3342
|
+
function renderTreeNode(node, depth) {
|
|
3343
|
+
const isExpanded = expanded.has(node.fullPath);
|
|
3344
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3345
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3192
3346
|
"div",
|
|
3193
3347
|
{
|
|
3194
|
-
className:
|
|
3195
|
-
|
|
3348
|
+
className: "flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2",
|
|
3349
|
+
style: { paddingLeft: `${depth * 16 + 8}px` },
|
|
3350
|
+
onClick: () => toggleExpand(node.fullPath),
|
|
3196
3351
|
children: [
|
|
3197
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3198
|
-
|
|
3352
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
|
|
3353
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
|
|
3354
|
+
node.name,
|
|
3355
|
+
"/"
|
|
3356
|
+
] }),
|
|
3357
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3199
3358
|
"button",
|
|
3200
3359
|
{
|
|
3201
|
-
type: "button",
|
|
3202
3360
|
onClick: (e) => {
|
|
3203
3361
|
e.stopPropagation();
|
|
3204
|
-
|
|
3362
|
+
removeDir(node.fullPath);
|
|
3205
3363
|
},
|
|
3206
|
-
className: "
|
|
3364
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3207
3365
|
children: "\xD7"
|
|
3208
3366
|
}
|
|
3209
3367
|
)
|
|
3210
3368
|
]
|
|
3211
|
-
},
|
|
3212
|
-
f.path
|
|
3213
|
-
)) }),
|
|
3214
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: selectedFile ? /* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[300px] animate-pulse bg-muted/50 rounded" }), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3215
|
-
CodeEditor,
|
|
3216
|
-
{
|
|
3217
|
-
value: selectedFile.content,
|
|
3218
|
-
onChange: (val) => updateFileContent(selectedFile.path, val),
|
|
3219
|
-
filename: selectedFile.path
|
|
3220
3369
|
}
|
|
3221
|
-
)
|
|
3370
|
+
),
|
|
3371
|
+
isExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3372
|
+
node.children.map((child) => renderTreeNode(child, depth + 1)),
|
|
3373
|
+
node.files.map((file) => {
|
|
3374
|
+
const fileName = file.path.split("/").pop() ?? file.path;
|
|
3375
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3376
|
+
"div",
|
|
3377
|
+
{
|
|
3378
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3379
|
+
style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
|
|
3380
|
+
onClick: () => setSelectedPath(file.path),
|
|
3381
|
+
children: [
|
|
3382
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: fileName }),
|
|
3383
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3384
|
+
"button",
|
|
3385
|
+
{
|
|
3386
|
+
onClick: (e) => {
|
|
3387
|
+
e.stopPropagation();
|
|
3388
|
+
removeFile(file.path);
|
|
3389
|
+
},
|
|
3390
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3391
|
+
children: "\xD7"
|
|
3392
|
+
}
|
|
3393
|
+
)
|
|
3394
|
+
]
|
|
3395
|
+
},
|
|
3396
|
+
file.path
|
|
3397
|
+
);
|
|
3398
|
+
}),
|
|
3399
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingLeft: `${(depth + 1) * 16 + 8}px` }, className: "py-1 pr-2", children: addingFileInDir === node.fullPath ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1", children: [
|
|
3400
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3401
|
+
Input,
|
|
3402
|
+
{
|
|
3403
|
+
value: newFileName,
|
|
3404
|
+
onChange: (e) => setNewFileName(e.target.value),
|
|
3405
|
+
placeholder: "file.md",
|
|
3406
|
+
className: "h-6 text-xs",
|
|
3407
|
+
onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
|
|
3408
|
+
autoFocus: true
|
|
3409
|
+
}
|
|
3410
|
+
),
|
|
3411
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
|
|
3412
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3413
|
+
"button",
|
|
3414
|
+
{
|
|
3415
|
+
onClick: () => {
|
|
3416
|
+
setAddingFileInDir(node.fullPath);
|
|
3417
|
+
setNewFileName("");
|
|
3418
|
+
},
|
|
3419
|
+
className: "text-xs text-primary hover:underline",
|
|
3420
|
+
children: "+ File"
|
|
3421
|
+
}
|
|
3422
|
+
) })
|
|
3423
|
+
] })
|
|
3424
|
+
] }, node.fullPath);
|
|
3425
|
+
}
|
|
3426
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3427
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
3428
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3429
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: title }),
|
|
3430
|
+
isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3431
|
+
readOnly && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3432
|
+
] }),
|
|
3433
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3434
|
+
] }),
|
|
3435
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
3437
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
|
|
3438
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
|
|
3439
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3440
|
+
"button",
|
|
3441
|
+
{
|
|
3442
|
+
onClick: () => setShowAddFolder(!showAddFolder),
|
|
3443
|
+
className: "text-xs text-primary hover:underline",
|
|
3444
|
+
children: [
|
|
3445
|
+
"+ ",
|
|
3446
|
+
addFolderLabel
|
|
3447
|
+
]
|
|
3448
|
+
}
|
|
3449
|
+
)
|
|
3450
|
+
] }),
|
|
3451
|
+
showAddFolder && !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
|
|
3452
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3453
|
+
Input,
|
|
3454
|
+
{
|
|
3455
|
+
value: newFolderName,
|
|
3456
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
3457
|
+
placeholder: "folder-name",
|
|
3458
|
+
className: "h-7 text-xs",
|
|
3459
|
+
onKeyDown: (e) => e.key === "Enter" && addFolder(),
|
|
3460
|
+
autoFocus: true
|
|
3461
|
+
}
|
|
3462
|
+
),
|
|
3463
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
|
|
3464
|
+
] }),
|
|
3465
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm overflow-y-auto", children: [
|
|
3466
|
+
tree.rootFiles.map((file) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3467
|
+
"div",
|
|
3468
|
+
{
|
|
3469
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3470
|
+
style: { paddingLeft: "8px" },
|
|
3471
|
+
onClick: () => setSelectedPath(file.path),
|
|
3472
|
+
children: [
|
|
3473
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: file.path }),
|
|
3474
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3475
|
+
"button",
|
|
3476
|
+
{
|
|
3477
|
+
onClick: (e) => {
|
|
3478
|
+
e.stopPropagation();
|
|
3479
|
+
removeFile(file.path);
|
|
3480
|
+
},
|
|
3481
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3482
|
+
children: "\xD7"
|
|
3483
|
+
}
|
|
3484
|
+
)
|
|
3485
|
+
]
|
|
3486
|
+
},
|
|
3487
|
+
file.path
|
|
3488
|
+
)),
|
|
3489
|
+
tree.rootDirs.map((node) => renderTreeNode(node, 0)),
|
|
3490
|
+
files.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-3 text-xs text-muted-foreground", children: readOnly ? "No files." : `No ${title.toLowerCase()} yet. Add a ${addFolderLabel.toLowerCase()} to get started.` })
|
|
3491
|
+
] })
|
|
3492
|
+
] }),
|
|
3493
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 border border-border rounded-md overflow-hidden", children: activeFile ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col", children: [
|
|
3494
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: activeFile.path }),
|
|
3495
|
+
/* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 animate-pulse bg-muted/50" }), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3496
|
+
CodeEditor,
|
|
3497
|
+
{
|
|
3498
|
+
value: activeFile.content,
|
|
3499
|
+
onChange: handleEditorChange,
|
|
3500
|
+
filename: activeFile.path
|
|
3501
|
+
}
|
|
3502
|
+
) })
|
|
3503
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
|
|
3504
|
+
"Select a file to ",
|
|
3505
|
+
readOnly ? "view" : "edit"
|
|
3506
|
+
] }) })
|
|
3222
3507
|
] })
|
|
3223
3508
|
] });
|
|
3224
3509
|
}
|
|
3510
|
+
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3511
|
+
const client = useAgentPlaneClient();
|
|
3512
|
+
const initialFiles = React3.useMemo(
|
|
3513
|
+
() => initialSkills.flatMap(
|
|
3514
|
+
(s) => s.files.map((f) => ({
|
|
3515
|
+
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3516
|
+
content: f.content
|
|
3517
|
+
}))
|
|
3518
|
+
),
|
|
3519
|
+
[initialSkills]
|
|
3520
|
+
);
|
|
3521
|
+
const handleSave = React3.useCallback(async (files) => {
|
|
3522
|
+
const folderMap = /* @__PURE__ */ new Map();
|
|
3523
|
+
for (const file of files) {
|
|
3524
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3525
|
+
if (slashIdx === -1) {
|
|
3526
|
+
const existing = folderMap.get("(root)") ?? [];
|
|
3527
|
+
existing.push({ path: file.path, content: file.content });
|
|
3528
|
+
folderMap.set("(root)", existing);
|
|
3529
|
+
} else {
|
|
3530
|
+
const folder = file.path.slice(0, slashIdx);
|
|
3531
|
+
const fileName = file.path.slice(slashIdx + 1);
|
|
3532
|
+
const existing = folderMap.get(folder) ?? [];
|
|
3533
|
+
existing.push({ path: fileName, content: file.content });
|
|
3534
|
+
folderMap.set(folder, existing);
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3538
|
+
await client.agents.update(agentId, { skills });
|
|
3539
|
+
onSaved?.();
|
|
3540
|
+
}, [agentId, client, onSaved]);
|
|
3541
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3542
|
+
FileTreeEditor,
|
|
3543
|
+
{
|
|
3544
|
+
initialFiles,
|
|
3545
|
+
onSave: handleSave,
|
|
3546
|
+
title: "Skills",
|
|
3547
|
+
saveLabel: "Save Skills",
|
|
3548
|
+
addFolderLabel: "Skill",
|
|
3549
|
+
newFileTemplate: {
|
|
3550
|
+
filename: "SKILL.md",
|
|
3551
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
);
|
|
3555
|
+
}
|
|
3225
3556
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3226
3557
|
const client = useAgentPlaneClient();
|
|
3227
3558
|
const [plugins, setPlugins] = React3.useState(initialPlugins);
|