@getcatalystiq/agent-plane-ui 0.1.5 → 0.1.6
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 +352 -92
- package/dist/index.d.cts +0 -4
- package/dist/index.d.ts +0 -4
- package/dist/index.js +352 -92
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3100,128 +3100,388 @@ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAl
|
|
|
3100
3100
|
] });
|
|
3101
3101
|
}
|
|
3102
3102
|
var CodeEditor = React3.lazy(() => import('./code-editor-ZD5ZILTM.cjs'));
|
|
3103
|
-
function
|
|
3104
|
-
const
|
|
3105
|
-
const
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3103
|
+
function buildTree(files) {
|
|
3104
|
+
const rootFiles = [];
|
|
3105
|
+
const dirMap = /* @__PURE__ */ new Map();
|
|
3106
|
+
function ensureDir(dirPath) {
|
|
3107
|
+
const existing = dirMap.get(dirPath);
|
|
3108
|
+
if (existing) return existing;
|
|
3109
|
+
const parts = dirPath.split("/");
|
|
3110
|
+
const node = {
|
|
3111
|
+
name: parts[parts.length - 1],
|
|
3112
|
+
fullPath: dirPath,
|
|
3113
|
+
children: [],
|
|
3114
|
+
files: []
|
|
3115
|
+
};
|
|
3116
|
+
dirMap.set(dirPath, node);
|
|
3117
|
+
if (parts.length > 1) {
|
|
3118
|
+
const parentPath = parts.slice(0, -1).join("/");
|
|
3119
|
+
const parent = ensureDir(parentPath);
|
|
3120
|
+
if (!parent.children.some((c) => c.fullPath === dirPath)) {
|
|
3121
|
+
parent.children.push(node);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
return node;
|
|
3125
|
+
}
|
|
3126
|
+
for (const file of files) {
|
|
3127
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3128
|
+
if (slashIdx === -1) {
|
|
3129
|
+
rootFiles.push(file);
|
|
3130
|
+
} else {
|
|
3131
|
+
const dirPath = file.path.slice(0, slashIdx);
|
|
3132
|
+
const dir = ensureDir(dirPath);
|
|
3133
|
+
dir.files.push(file);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
const topLevel = [];
|
|
3137
|
+
for (const node of dirMap.values()) {
|
|
3138
|
+
if (!node.fullPath.includes("/")) {
|
|
3139
|
+
topLevel.push(node);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
function sortNode(node) {
|
|
3143
|
+
node.children.sort((a, b) => a.name.localeCompare(b.name));
|
|
3144
|
+
node.files.sort((a, b) => a.path.localeCompare(b.path));
|
|
3145
|
+
node.children.forEach(sortNode);
|
|
3146
|
+
}
|
|
3147
|
+
topLevel.forEach(sortNode);
|
|
3148
|
+
topLevel.sort((a, b) => a.name.localeCompare(b.name));
|
|
3149
|
+
rootFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
3150
|
+
return { rootFiles, rootDirs: topLevel };
|
|
3151
|
+
}
|
|
3152
|
+
function collectAllDirPaths(nodes) {
|
|
3153
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3154
|
+
function walk(node) {
|
|
3155
|
+
paths.add(node.fullPath);
|
|
3156
|
+
node.children.forEach(walk);
|
|
3157
|
+
}
|
|
3158
|
+
nodes.forEach(walk);
|
|
3159
|
+
return paths;
|
|
3160
|
+
}
|
|
3161
|
+
function FileTreeEditor({
|
|
3162
|
+
initialFiles,
|
|
3163
|
+
onSave,
|
|
3164
|
+
onChange,
|
|
3165
|
+
readOnly = false,
|
|
3166
|
+
hideSave = false,
|
|
3167
|
+
title = "Files",
|
|
3168
|
+
saveLabel = "Save",
|
|
3169
|
+
addFolderLabel = "Folder",
|
|
3170
|
+
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" },
|
|
3171
|
+
savedVersion
|
|
3172
|
+
}) {
|
|
3114
3173
|
const [files, setFiles] = React3.useState(initialFiles);
|
|
3115
|
-
const [selectedPath, setSelectedPath] = React3.useState(
|
|
3174
|
+
const [selectedPath, setSelectedPath] = React3.useState(
|
|
3175
|
+
initialFiles.length > 0 ? initialFiles[0].path : null
|
|
3176
|
+
);
|
|
3116
3177
|
const [saving, setSaving] = React3.useState(false);
|
|
3117
|
-
const [
|
|
3178
|
+
const [expanded, setExpanded] = React3.useState(() => {
|
|
3179
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3180
|
+
return collectAllDirPaths(rootDirs);
|
|
3181
|
+
});
|
|
3182
|
+
const [showAddFolder, setShowAddFolder] = React3.useState(false);
|
|
3183
|
+
const [newFolderName, setNewFolderName] = React3.useState("");
|
|
3184
|
+
const [addingFileInDir, setAddingFileInDir] = React3.useState(null);
|
|
3118
3185
|
const [newFileName, setNewFileName] = React3.useState("");
|
|
3186
|
+
const [savedSnapshot, setSavedSnapshot] = React3.useState(() => JSON.stringify(initialFiles));
|
|
3187
|
+
React3.useEffect(() => {
|
|
3188
|
+
const snap = JSON.stringify(initialFiles);
|
|
3189
|
+
setSavedSnapshot(snap);
|
|
3190
|
+
setFiles(initialFiles);
|
|
3191
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3192
|
+
setExpanded(collectAllDirPaths(rootDirs));
|
|
3193
|
+
}, [initialFiles]);
|
|
3194
|
+
React3.useEffect(() => {
|
|
3195
|
+
if (savedVersion !== void 0 && savedVersion > 0) {
|
|
3196
|
+
setSavedSnapshot(JSON.stringify(files));
|
|
3197
|
+
}
|
|
3198
|
+
}, [savedVersion]);
|
|
3199
|
+
const onChangeRef = React3.useRef(onChange);
|
|
3200
|
+
onChangeRef.current = onChange;
|
|
3201
|
+
React3.useEffect(() => {
|
|
3202
|
+
if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
|
|
3203
|
+
onChangeRef.current(files);
|
|
3204
|
+
}
|
|
3205
|
+
}, [files, savedSnapshot]);
|
|
3119
3206
|
const isDirty = React3.useMemo(
|
|
3120
|
-
() => JSON.stringify(files) !==
|
|
3121
|
-
[files,
|
|
3207
|
+
() => JSON.stringify(files) !== savedSnapshot,
|
|
3208
|
+
[files, savedSnapshot]
|
|
3209
|
+
);
|
|
3210
|
+
const tree = React3.useMemo(() => buildTree(files), [files]);
|
|
3211
|
+
const activeFile = React3.useMemo(
|
|
3212
|
+
() => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
|
|
3213
|
+
[files, selectedPath]
|
|
3122
3214
|
);
|
|
3123
|
-
const
|
|
3124
|
-
|
|
3125
|
-
setFiles((prev) => prev.map((f) => f.path ===
|
|
3215
|
+
const handleEditorChange = React3.useCallback((value) => {
|
|
3216
|
+
if (readOnly || !selectedPath) return;
|
|
3217
|
+
setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
|
|
3218
|
+
}, [readOnly, selectedPath]);
|
|
3219
|
+
function toggleExpand(dirPath) {
|
|
3220
|
+
setExpanded((prev) => {
|
|
3221
|
+
const next = new Set(prev);
|
|
3222
|
+
if (next.has(dirPath)) next.delete(dirPath);
|
|
3223
|
+
else next.add(dirPath);
|
|
3224
|
+
return next;
|
|
3225
|
+
});
|
|
3226
|
+
}
|
|
3227
|
+
function addFolder() {
|
|
3228
|
+
const name = newFolderName.trim();
|
|
3229
|
+
if (!name) return;
|
|
3230
|
+
const filePath = `${name}/${newFileTemplate.filename}`;
|
|
3231
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3232
|
+
const content = newFileTemplate.content.replace("# New", `# ${name}`);
|
|
3233
|
+
setFiles((prev) => [...prev, { path: filePath, content }]);
|
|
3234
|
+
setSelectedPath(filePath);
|
|
3235
|
+
setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
|
|
3236
|
+
setNewFolderName("");
|
|
3237
|
+
setShowAddFolder(false);
|
|
3238
|
+
}
|
|
3239
|
+
function removeDir(dirPath) {
|
|
3240
|
+
const prefix = dirPath + "/";
|
|
3241
|
+
const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
|
|
3242
|
+
if (affectedFiles.length === 0) return;
|
|
3243
|
+
if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
|
|
3244
|
+
setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
|
|
3245
|
+
if (selectedPath && selectedPath.startsWith(prefix)) setSelectedPath(null);
|
|
3126
3246
|
}
|
|
3127
|
-
function
|
|
3247
|
+
function removeFile(filePath) {
|
|
3248
|
+
const fileName = filePath.split("/").pop() ?? filePath;
|
|
3249
|
+
if (!confirm(`Remove file "${fileName}"?`)) return;
|
|
3250
|
+
setFiles((prev) => prev.filter((f) => f.path !== filePath));
|
|
3251
|
+
if (selectedPath === filePath) setSelectedPath(null);
|
|
3252
|
+
}
|
|
3253
|
+
function addFileInDir(dirPath) {
|
|
3128
3254
|
const name = newFileName.trim();
|
|
3129
|
-
if (!name
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3255
|
+
if (!name) return;
|
|
3256
|
+
const filePath = dirPath ? `${dirPath}/${name}` : name;
|
|
3257
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3258
|
+
setFiles((prev) => [...prev, { path: filePath, content: "" }]);
|
|
3259
|
+
setSelectedPath(filePath);
|
|
3134
3260
|
setNewFileName("");
|
|
3261
|
+
setAddingFileInDir(null);
|
|
3135
3262
|
}
|
|
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 () => {
|
|
3263
|
+
async function handleSave() {
|
|
3143
3264
|
setSaving(true);
|
|
3144
3265
|
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?.();
|
|
3266
|
+
await onSave(files);
|
|
3163
3267
|
} finally {
|
|
3164
3268
|
setSaving(false);
|
|
3165
3269
|
}
|
|
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(
|
|
3270
|
+
}
|
|
3271
|
+
function renderTreeNode(node, depth) {
|
|
3272
|
+
const isExpanded = expanded.has(node.fullPath);
|
|
3273
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3274
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3192
3275
|
"div",
|
|
3193
3276
|
{
|
|
3194
|
-
className:
|
|
3195
|
-
|
|
3277
|
+
className: "flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2",
|
|
3278
|
+
style: { paddingLeft: `${depth * 16 + 8}px` },
|
|
3279
|
+
onClick: () => toggleExpand(node.fullPath),
|
|
3196
3280
|
children: [
|
|
3197
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3198
|
-
|
|
3281
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
|
|
3282
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
|
|
3283
|
+
node.name,
|
|
3284
|
+
"/"
|
|
3285
|
+
] }),
|
|
3286
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3199
3287
|
"button",
|
|
3200
3288
|
{
|
|
3201
|
-
type: "button",
|
|
3202
3289
|
onClick: (e) => {
|
|
3203
3290
|
e.stopPropagation();
|
|
3204
|
-
|
|
3291
|
+
removeDir(node.fullPath);
|
|
3205
3292
|
},
|
|
3206
|
-
className: "
|
|
3293
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3207
3294
|
children: "\xD7"
|
|
3208
3295
|
}
|
|
3209
3296
|
)
|
|
3210
3297
|
]
|
|
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
3298
|
}
|
|
3221
|
-
)
|
|
3299
|
+
),
|
|
3300
|
+
isExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3301
|
+
node.children.map((child) => renderTreeNode(child, depth + 1)),
|
|
3302
|
+
node.files.map((file) => {
|
|
3303
|
+
const fileName = file.path.split("/").pop() ?? file.path;
|
|
3304
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3305
|
+
"div",
|
|
3306
|
+
{
|
|
3307
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3308
|
+
style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
|
|
3309
|
+
onClick: () => setSelectedPath(file.path),
|
|
3310
|
+
children: [
|
|
3311
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: fileName }),
|
|
3312
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3313
|
+
"button",
|
|
3314
|
+
{
|
|
3315
|
+
onClick: (e) => {
|
|
3316
|
+
e.stopPropagation();
|
|
3317
|
+
removeFile(file.path);
|
|
3318
|
+
},
|
|
3319
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3320
|
+
children: "\xD7"
|
|
3321
|
+
}
|
|
3322
|
+
)
|
|
3323
|
+
]
|
|
3324
|
+
},
|
|
3325
|
+
file.path
|
|
3326
|
+
);
|
|
3327
|
+
}),
|
|
3328
|
+
!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: [
|
|
3329
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3330
|
+
Input,
|
|
3331
|
+
{
|
|
3332
|
+
value: newFileName,
|
|
3333
|
+
onChange: (e) => setNewFileName(e.target.value),
|
|
3334
|
+
placeholder: "file.md",
|
|
3335
|
+
className: "h-6 text-xs",
|
|
3336
|
+
onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
|
|
3337
|
+
autoFocus: true
|
|
3338
|
+
}
|
|
3339
|
+
),
|
|
3340
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
|
|
3341
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3342
|
+
"button",
|
|
3343
|
+
{
|
|
3344
|
+
onClick: () => {
|
|
3345
|
+
setAddingFileInDir(node.fullPath);
|
|
3346
|
+
setNewFileName("");
|
|
3347
|
+
},
|
|
3348
|
+
className: "text-xs text-primary hover:underline",
|
|
3349
|
+
children: "+ File"
|
|
3350
|
+
}
|
|
3351
|
+
) })
|
|
3352
|
+
] })
|
|
3353
|
+
] }, node.fullPath);
|
|
3354
|
+
}
|
|
3355
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3356
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
3357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3358
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: title }),
|
|
3359
|
+
isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3360
|
+
readOnly && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3361
|
+
] }),
|
|
3362
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3363
|
+
] }),
|
|
3364
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3365
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
3366
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
|
|
3367
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
|
|
3368
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
onClick: () => setShowAddFolder(!showAddFolder),
|
|
3372
|
+
className: "text-xs text-primary hover:underline",
|
|
3373
|
+
children: [
|
|
3374
|
+
"+ ",
|
|
3375
|
+
addFolderLabel
|
|
3376
|
+
]
|
|
3377
|
+
}
|
|
3378
|
+
)
|
|
3379
|
+
] }),
|
|
3380
|
+
showAddFolder && !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
|
|
3381
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3382
|
+
Input,
|
|
3383
|
+
{
|
|
3384
|
+
value: newFolderName,
|
|
3385
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
3386
|
+
placeholder: "folder-name",
|
|
3387
|
+
className: "h-7 text-xs",
|
|
3388
|
+
onKeyDown: (e) => e.key === "Enter" && addFolder(),
|
|
3389
|
+
autoFocus: true
|
|
3390
|
+
}
|
|
3391
|
+
),
|
|
3392
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
|
|
3393
|
+
] }),
|
|
3394
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm overflow-y-auto", children: [
|
|
3395
|
+
tree.rootFiles.map((file) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3396
|
+
"div",
|
|
3397
|
+
{
|
|
3398
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3399
|
+
style: { paddingLeft: "8px" },
|
|
3400
|
+
onClick: () => setSelectedPath(file.path),
|
|
3401
|
+
children: [
|
|
3402
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: file.path }),
|
|
3403
|
+
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3404
|
+
"button",
|
|
3405
|
+
{
|
|
3406
|
+
onClick: (e) => {
|
|
3407
|
+
e.stopPropagation();
|
|
3408
|
+
removeFile(file.path);
|
|
3409
|
+
},
|
|
3410
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3411
|
+
children: "\xD7"
|
|
3412
|
+
}
|
|
3413
|
+
)
|
|
3414
|
+
]
|
|
3415
|
+
},
|
|
3416
|
+
file.path
|
|
3417
|
+
)),
|
|
3418
|
+
tree.rootDirs.map((node) => renderTreeNode(node, 0)),
|
|
3419
|
+
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.` })
|
|
3420
|
+
] })
|
|
3421
|
+
] }),
|
|
3422
|
+
/* @__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: [
|
|
3423
|
+
/* @__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 }),
|
|
3424
|
+
/* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 animate-pulse bg-muted/50" }), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3425
|
+
CodeEditor,
|
|
3426
|
+
{
|
|
3427
|
+
value: activeFile.content,
|
|
3428
|
+
onChange: handleEditorChange,
|
|
3429
|
+
filename: activeFile.path
|
|
3430
|
+
}
|
|
3431
|
+
) })
|
|
3432
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
|
|
3433
|
+
"Select a file to ",
|
|
3434
|
+
readOnly ? "view" : "edit"
|
|
3435
|
+
] }) })
|
|
3222
3436
|
] })
|
|
3223
3437
|
] });
|
|
3224
3438
|
}
|
|
3439
|
+
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3440
|
+
const client = useAgentPlaneClient();
|
|
3441
|
+
const initialFiles = React3.useMemo(
|
|
3442
|
+
() => initialSkills.flatMap(
|
|
3443
|
+
(s) => s.files.map((f) => ({
|
|
3444
|
+
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3445
|
+
content: f.content
|
|
3446
|
+
}))
|
|
3447
|
+
),
|
|
3448
|
+
[initialSkills]
|
|
3449
|
+
);
|
|
3450
|
+
const handleSave = React3.useCallback(async (files) => {
|
|
3451
|
+
const folderMap = /* @__PURE__ */ new Map();
|
|
3452
|
+
for (const file of files) {
|
|
3453
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3454
|
+
if (slashIdx === -1) {
|
|
3455
|
+
const existing = folderMap.get("(root)") ?? [];
|
|
3456
|
+
existing.push({ path: file.path, content: file.content });
|
|
3457
|
+
folderMap.set("(root)", existing);
|
|
3458
|
+
} else {
|
|
3459
|
+
const folder = file.path.slice(0, slashIdx);
|
|
3460
|
+
const fileName = file.path.slice(slashIdx + 1);
|
|
3461
|
+
const existing = folderMap.get(folder) ?? [];
|
|
3462
|
+
existing.push({ path: fileName, content: file.content });
|
|
3463
|
+
folderMap.set(folder, existing);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3467
|
+
await client.agents.update(agentId, { skills });
|
|
3468
|
+
onSaved?.();
|
|
3469
|
+
}, [agentId, client, onSaved]);
|
|
3470
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3471
|
+
FileTreeEditor,
|
|
3472
|
+
{
|
|
3473
|
+
initialFiles,
|
|
3474
|
+
onSave: handleSave,
|
|
3475
|
+
title: "Skills",
|
|
3476
|
+
saveLabel: "Save Skills",
|
|
3477
|
+
addFolderLabel: "Skill",
|
|
3478
|
+
newFileTemplate: {
|
|
3479
|
+
filename: "SKILL.md",
|
|
3480
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
);
|
|
3484
|
+
}
|
|
3225
3485
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3226
3486
|
const client = useAgentPlaneClient();
|
|
3227
3487
|
const [plugins, setPlugins] = React3.useState(initialPlugins);
|
package/dist/index.d.cts
CHANGED
|
@@ -566,10 +566,6 @@ interface Props$5 {
|
|
|
566
566
|
initialSkills: AgentSkill[];
|
|
567
567
|
onSaved?: () => void;
|
|
568
568
|
}
|
|
569
|
-
/**
|
|
570
|
-
* Simplified skills editor that works without CodeMirror (which is a heavy
|
|
571
|
-
* dependency). Uses plain textareas for editing skill content.
|
|
572
|
-
*/
|
|
573
569
|
declare function AgentSkillManager({ agentId, initialSkills, onSaved }: Props$5): react_jsx_runtime.JSX.Element;
|
|
574
570
|
|
|
575
571
|
interface AgentPlugin {
|
package/dist/index.d.ts
CHANGED
|
@@ -566,10 +566,6 @@ interface Props$5 {
|
|
|
566
566
|
initialSkills: AgentSkill[];
|
|
567
567
|
onSaved?: () => void;
|
|
568
568
|
}
|
|
569
|
-
/**
|
|
570
|
-
* Simplified skills editor that works without CodeMirror (which is a heavy
|
|
571
|
-
* dependency). Uses plain textareas for editing skill content.
|
|
572
|
-
*/
|
|
573
569
|
declare function AgentSkillManager({ agentId, initialSkills, onSaved }: Props$5): react_jsx_runtime.JSX.Element;
|
|
574
570
|
|
|
575
571
|
interface AgentPlugin {
|
package/dist/index.js
CHANGED
|
@@ -3074,128 +3074,388 @@ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAl
|
|
|
3074
3074
|
] });
|
|
3075
3075
|
}
|
|
3076
3076
|
var CodeEditor = lazy(() => import('./code-editor-E7L6Y3LM.js'));
|
|
3077
|
-
function
|
|
3078
|
-
const
|
|
3079
|
-
const
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3077
|
+
function buildTree(files) {
|
|
3078
|
+
const rootFiles = [];
|
|
3079
|
+
const dirMap = /* @__PURE__ */ new Map();
|
|
3080
|
+
function ensureDir(dirPath) {
|
|
3081
|
+
const existing = dirMap.get(dirPath);
|
|
3082
|
+
if (existing) return existing;
|
|
3083
|
+
const parts = dirPath.split("/");
|
|
3084
|
+
const node = {
|
|
3085
|
+
name: parts[parts.length - 1],
|
|
3086
|
+
fullPath: dirPath,
|
|
3087
|
+
children: [],
|
|
3088
|
+
files: []
|
|
3089
|
+
};
|
|
3090
|
+
dirMap.set(dirPath, node);
|
|
3091
|
+
if (parts.length > 1) {
|
|
3092
|
+
const parentPath = parts.slice(0, -1).join("/");
|
|
3093
|
+
const parent = ensureDir(parentPath);
|
|
3094
|
+
if (!parent.children.some((c) => c.fullPath === dirPath)) {
|
|
3095
|
+
parent.children.push(node);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
return node;
|
|
3099
|
+
}
|
|
3100
|
+
for (const file of files) {
|
|
3101
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3102
|
+
if (slashIdx === -1) {
|
|
3103
|
+
rootFiles.push(file);
|
|
3104
|
+
} else {
|
|
3105
|
+
const dirPath = file.path.slice(0, slashIdx);
|
|
3106
|
+
const dir = ensureDir(dirPath);
|
|
3107
|
+
dir.files.push(file);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
const topLevel = [];
|
|
3111
|
+
for (const node of dirMap.values()) {
|
|
3112
|
+
if (!node.fullPath.includes("/")) {
|
|
3113
|
+
topLevel.push(node);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
function sortNode(node) {
|
|
3117
|
+
node.children.sort((a, b) => a.name.localeCompare(b.name));
|
|
3118
|
+
node.files.sort((a, b) => a.path.localeCompare(b.path));
|
|
3119
|
+
node.children.forEach(sortNode);
|
|
3120
|
+
}
|
|
3121
|
+
topLevel.forEach(sortNode);
|
|
3122
|
+
topLevel.sort((a, b) => a.name.localeCompare(b.name));
|
|
3123
|
+
rootFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
3124
|
+
return { rootFiles, rootDirs: topLevel };
|
|
3125
|
+
}
|
|
3126
|
+
function collectAllDirPaths(nodes) {
|
|
3127
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3128
|
+
function walk(node) {
|
|
3129
|
+
paths.add(node.fullPath);
|
|
3130
|
+
node.children.forEach(walk);
|
|
3131
|
+
}
|
|
3132
|
+
nodes.forEach(walk);
|
|
3133
|
+
return paths;
|
|
3134
|
+
}
|
|
3135
|
+
function FileTreeEditor({
|
|
3136
|
+
initialFiles,
|
|
3137
|
+
onSave,
|
|
3138
|
+
onChange,
|
|
3139
|
+
readOnly = false,
|
|
3140
|
+
hideSave = false,
|
|
3141
|
+
title = "Files",
|
|
3142
|
+
saveLabel = "Save",
|
|
3143
|
+
addFolderLabel = "Folder",
|
|
3144
|
+
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" },
|
|
3145
|
+
savedVersion
|
|
3146
|
+
}) {
|
|
3088
3147
|
const [files, setFiles] = useState(initialFiles);
|
|
3089
|
-
const [selectedPath, setSelectedPath] = useState(
|
|
3148
|
+
const [selectedPath, setSelectedPath] = useState(
|
|
3149
|
+
initialFiles.length > 0 ? initialFiles[0].path : null
|
|
3150
|
+
);
|
|
3090
3151
|
const [saving, setSaving] = useState(false);
|
|
3091
|
-
const [
|
|
3152
|
+
const [expanded, setExpanded] = useState(() => {
|
|
3153
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3154
|
+
return collectAllDirPaths(rootDirs);
|
|
3155
|
+
});
|
|
3156
|
+
const [showAddFolder, setShowAddFolder] = useState(false);
|
|
3157
|
+
const [newFolderName, setNewFolderName] = useState("");
|
|
3158
|
+
const [addingFileInDir, setAddingFileInDir] = useState(null);
|
|
3092
3159
|
const [newFileName, setNewFileName] = useState("");
|
|
3160
|
+
const [savedSnapshot, setSavedSnapshot] = useState(() => JSON.stringify(initialFiles));
|
|
3161
|
+
useEffect(() => {
|
|
3162
|
+
const snap = JSON.stringify(initialFiles);
|
|
3163
|
+
setSavedSnapshot(snap);
|
|
3164
|
+
setFiles(initialFiles);
|
|
3165
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3166
|
+
setExpanded(collectAllDirPaths(rootDirs));
|
|
3167
|
+
}, [initialFiles]);
|
|
3168
|
+
useEffect(() => {
|
|
3169
|
+
if (savedVersion !== void 0 && savedVersion > 0) {
|
|
3170
|
+
setSavedSnapshot(JSON.stringify(files));
|
|
3171
|
+
}
|
|
3172
|
+
}, [savedVersion]);
|
|
3173
|
+
const onChangeRef = useRef(onChange);
|
|
3174
|
+
onChangeRef.current = onChange;
|
|
3175
|
+
useEffect(() => {
|
|
3176
|
+
if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
|
|
3177
|
+
onChangeRef.current(files);
|
|
3178
|
+
}
|
|
3179
|
+
}, [files, savedSnapshot]);
|
|
3093
3180
|
const isDirty = useMemo(
|
|
3094
|
-
() => JSON.stringify(files) !==
|
|
3095
|
-
[files,
|
|
3181
|
+
() => JSON.stringify(files) !== savedSnapshot,
|
|
3182
|
+
[files, savedSnapshot]
|
|
3183
|
+
);
|
|
3184
|
+
const tree = useMemo(() => buildTree(files), [files]);
|
|
3185
|
+
const activeFile = useMemo(
|
|
3186
|
+
() => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
|
|
3187
|
+
[files, selectedPath]
|
|
3096
3188
|
);
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
setFiles((prev) => prev.map((f) => f.path ===
|
|
3189
|
+
const handleEditorChange = useCallback((value) => {
|
|
3190
|
+
if (readOnly || !selectedPath) return;
|
|
3191
|
+
setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
|
|
3192
|
+
}, [readOnly, selectedPath]);
|
|
3193
|
+
function toggleExpand(dirPath) {
|
|
3194
|
+
setExpanded((prev) => {
|
|
3195
|
+
const next = new Set(prev);
|
|
3196
|
+
if (next.has(dirPath)) next.delete(dirPath);
|
|
3197
|
+
else next.add(dirPath);
|
|
3198
|
+
return next;
|
|
3199
|
+
});
|
|
3200
|
+
}
|
|
3201
|
+
function addFolder() {
|
|
3202
|
+
const name = newFolderName.trim();
|
|
3203
|
+
if (!name) return;
|
|
3204
|
+
const filePath = `${name}/${newFileTemplate.filename}`;
|
|
3205
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3206
|
+
const content = newFileTemplate.content.replace("# New", `# ${name}`);
|
|
3207
|
+
setFiles((prev) => [...prev, { path: filePath, content }]);
|
|
3208
|
+
setSelectedPath(filePath);
|
|
3209
|
+
setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
|
|
3210
|
+
setNewFolderName("");
|
|
3211
|
+
setShowAddFolder(false);
|
|
3212
|
+
}
|
|
3213
|
+
function removeDir(dirPath) {
|
|
3214
|
+
const prefix = dirPath + "/";
|
|
3215
|
+
const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
|
|
3216
|
+
if (affectedFiles.length === 0) return;
|
|
3217
|
+
if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
|
|
3218
|
+
setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
|
|
3219
|
+
if (selectedPath && selectedPath.startsWith(prefix)) setSelectedPath(null);
|
|
3100
3220
|
}
|
|
3101
|
-
function
|
|
3221
|
+
function removeFile(filePath) {
|
|
3222
|
+
const fileName = filePath.split("/").pop() ?? filePath;
|
|
3223
|
+
if (!confirm(`Remove file "${fileName}"?`)) return;
|
|
3224
|
+
setFiles((prev) => prev.filter((f) => f.path !== filePath));
|
|
3225
|
+
if (selectedPath === filePath) setSelectedPath(null);
|
|
3226
|
+
}
|
|
3227
|
+
function addFileInDir(dirPath) {
|
|
3102
3228
|
const name = newFileName.trim();
|
|
3103
|
-
if (!name
|
|
3104
|
-
const
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3229
|
+
if (!name) return;
|
|
3230
|
+
const filePath = dirPath ? `${dirPath}/${name}` : name;
|
|
3231
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3232
|
+
setFiles((prev) => [...prev, { path: filePath, content: "" }]);
|
|
3233
|
+
setSelectedPath(filePath);
|
|
3108
3234
|
setNewFileName("");
|
|
3235
|
+
setAddingFileInDir(null);
|
|
3109
3236
|
}
|
|
3110
|
-
function
|
|
3111
|
-
setFiles((prev) => prev.filter((f) => f.path !== path));
|
|
3112
|
-
if (selectedPath === path) {
|
|
3113
|
-
setSelectedPath(files.find((f) => f.path !== path)?.path ?? null);
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
const handleSave = useCallback(async () => {
|
|
3237
|
+
async function handleSave() {
|
|
3117
3238
|
setSaving(true);
|
|
3118
3239
|
try {
|
|
3119
|
-
|
|
3120
|
-
for (const file of files) {
|
|
3121
|
-
const slashIdx = file.path.lastIndexOf("/");
|
|
3122
|
-
if (slashIdx === -1) {
|
|
3123
|
-
const existing = folderMap.get("(root)") ?? [];
|
|
3124
|
-
existing.push({ path: file.path, content: file.content });
|
|
3125
|
-
folderMap.set("(root)", existing);
|
|
3126
|
-
} else {
|
|
3127
|
-
const folder = file.path.slice(0, slashIdx);
|
|
3128
|
-
const fileName = file.path.slice(slashIdx + 1);
|
|
3129
|
-
const existing = folderMap.get(folder) ?? [];
|
|
3130
|
-
existing.push({ path: fileName, content: file.content });
|
|
3131
|
-
folderMap.set(folder, existing);
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3135
|
-
await client.agents.update(agentId, { skills });
|
|
3136
|
-
onSaved?.();
|
|
3240
|
+
await onSave(files);
|
|
3137
3241
|
} finally {
|
|
3138
3242
|
setSaving(false);
|
|
3139
3243
|
}
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
/* @__PURE__ */
|
|
3145
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || !isDirty, children: saving ? "Saving..." : "Save Skills" })
|
|
3146
|
-
] }) }),
|
|
3147
|
-
addingFile && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
|
|
3148
|
-
/* @__PURE__ */ jsx(
|
|
3149
|
-
Input,
|
|
3150
|
-
{
|
|
3151
|
-
value: newFileName,
|
|
3152
|
-
onChange: (e) => setNewFileName(e.target.value),
|
|
3153
|
-
placeholder: "folder/SKILL.md",
|
|
3154
|
-
className: "max-w-xs text-sm",
|
|
3155
|
-
onKeyDown: (e) => e.key === "Enter" && addFile()
|
|
3156
|
-
}
|
|
3157
|
-
),
|
|
3158
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: addFile, children: "Add" }),
|
|
3159
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
|
|
3160
|
-
setAddingFile(false);
|
|
3161
|
-
setNewFileName("");
|
|
3162
|
-
}, children: "Cancel" })
|
|
3163
|
-
] }),
|
|
3164
|
-
files.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: 'No skills defined. Click "Add File" to create a skill.' }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[300px]", children: [
|
|
3165
|
-
/* @__PURE__ */ jsx("div", { className: "w-48 shrink-0 border-r border-border pr-3 space-y-1", children: files.map((f) => /* @__PURE__ */ jsxs(
|
|
3244
|
+
}
|
|
3245
|
+
function renderTreeNode(node, depth) {
|
|
3246
|
+
const isExpanded = expanded.has(node.fullPath);
|
|
3247
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3248
|
+
/* @__PURE__ */ jsxs(
|
|
3166
3249
|
"div",
|
|
3167
3250
|
{
|
|
3168
|
-
className:
|
|
3169
|
-
|
|
3251
|
+
className: "flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2",
|
|
3252
|
+
style: { paddingLeft: `${depth * 16 + 8}px` },
|
|
3253
|
+
onClick: () => toggleExpand(node.fullPath),
|
|
3170
3254
|
children: [
|
|
3171
|
-
/* @__PURE__ */
|
|
3172
|
-
|
|
3255
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
|
|
3256
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
|
|
3257
|
+
node.name,
|
|
3258
|
+
"/"
|
|
3259
|
+
] }),
|
|
3260
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3173
3261
|
"button",
|
|
3174
3262
|
{
|
|
3175
|
-
type: "button",
|
|
3176
3263
|
onClick: (e) => {
|
|
3177
3264
|
e.stopPropagation();
|
|
3178
|
-
|
|
3265
|
+
removeDir(node.fullPath);
|
|
3179
3266
|
},
|
|
3180
|
-
className: "
|
|
3267
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3181
3268
|
children: "\xD7"
|
|
3182
3269
|
}
|
|
3183
3270
|
)
|
|
3184
3271
|
]
|
|
3185
|
-
},
|
|
3186
|
-
f.path
|
|
3187
|
-
)) }),
|
|
3188
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: selectedFile ? /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "h-[300px] animate-pulse bg-muted/50 rounded" }), children: /* @__PURE__ */ jsx(
|
|
3189
|
-
CodeEditor,
|
|
3190
|
-
{
|
|
3191
|
-
value: selectedFile.content,
|
|
3192
|
-
onChange: (val) => updateFileContent(selectedFile.path, val),
|
|
3193
|
-
filename: selectedFile.path
|
|
3194
3272
|
}
|
|
3195
|
-
)
|
|
3273
|
+
),
|
|
3274
|
+
isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3275
|
+
node.children.map((child) => renderTreeNode(child, depth + 1)),
|
|
3276
|
+
node.files.map((file) => {
|
|
3277
|
+
const fileName = file.path.split("/").pop() ?? file.path;
|
|
3278
|
+
return /* @__PURE__ */ jsxs(
|
|
3279
|
+
"div",
|
|
3280
|
+
{
|
|
3281
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3282
|
+
style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
|
|
3283
|
+
onClick: () => setSelectedPath(file.path),
|
|
3284
|
+
children: [
|
|
3285
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: fileName }),
|
|
3286
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3287
|
+
"button",
|
|
3288
|
+
{
|
|
3289
|
+
onClick: (e) => {
|
|
3290
|
+
e.stopPropagation();
|
|
3291
|
+
removeFile(file.path);
|
|
3292
|
+
},
|
|
3293
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3294
|
+
children: "\xD7"
|
|
3295
|
+
}
|
|
3296
|
+
)
|
|
3297
|
+
]
|
|
3298
|
+
},
|
|
3299
|
+
file.path
|
|
3300
|
+
);
|
|
3301
|
+
}),
|
|
3302
|
+
!readOnly && /* @__PURE__ */ jsx("div", { style: { paddingLeft: `${(depth + 1) * 16 + 8}px` }, className: "py-1 pr-2", children: addingFileInDir === node.fullPath ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
3303
|
+
/* @__PURE__ */ jsx(
|
|
3304
|
+
Input,
|
|
3305
|
+
{
|
|
3306
|
+
value: newFileName,
|
|
3307
|
+
onChange: (e) => setNewFileName(e.target.value),
|
|
3308
|
+
placeholder: "file.md",
|
|
3309
|
+
className: "h-6 text-xs",
|
|
3310
|
+
onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
|
|
3311
|
+
autoFocus: true
|
|
3312
|
+
}
|
|
3313
|
+
),
|
|
3314
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
|
|
3315
|
+
] }) : /* @__PURE__ */ jsx(
|
|
3316
|
+
"button",
|
|
3317
|
+
{
|
|
3318
|
+
onClick: () => {
|
|
3319
|
+
setAddingFileInDir(node.fullPath);
|
|
3320
|
+
setNewFileName("");
|
|
3321
|
+
},
|
|
3322
|
+
className: "text-xs text-primary hover:underline",
|
|
3323
|
+
children: "+ File"
|
|
3324
|
+
}
|
|
3325
|
+
) })
|
|
3326
|
+
] })
|
|
3327
|
+
] }, node.fullPath);
|
|
3328
|
+
}
|
|
3329
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3330
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
3331
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3332
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: title }),
|
|
3333
|
+
isDirty && !readOnly && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3334
|
+
readOnly && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3335
|
+
] }),
|
|
3336
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3337
|
+
] }),
|
|
3338
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3339
|
+
/* @__PURE__ */ jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
3340
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
|
|
3341
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
|
|
3342
|
+
!readOnly && /* @__PURE__ */ jsxs(
|
|
3343
|
+
"button",
|
|
3344
|
+
{
|
|
3345
|
+
onClick: () => setShowAddFolder(!showAddFolder),
|
|
3346
|
+
className: "text-xs text-primary hover:underline",
|
|
3347
|
+
children: [
|
|
3348
|
+
"+ ",
|
|
3349
|
+
addFolderLabel
|
|
3350
|
+
]
|
|
3351
|
+
}
|
|
3352
|
+
)
|
|
3353
|
+
] }),
|
|
3354
|
+
showAddFolder && !readOnly && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
|
|
3355
|
+
/* @__PURE__ */ jsx(
|
|
3356
|
+
Input,
|
|
3357
|
+
{
|
|
3358
|
+
value: newFolderName,
|
|
3359
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
3360
|
+
placeholder: "folder-name",
|
|
3361
|
+
className: "h-7 text-xs",
|
|
3362
|
+
onKeyDown: (e) => e.key === "Enter" && addFolder(),
|
|
3363
|
+
autoFocus: true
|
|
3364
|
+
}
|
|
3365
|
+
),
|
|
3366
|
+
/* @__PURE__ */ jsx(Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
|
|
3367
|
+
] }),
|
|
3368
|
+
/* @__PURE__ */ jsxs("div", { className: "text-sm overflow-y-auto", children: [
|
|
3369
|
+
tree.rootFiles.map((file) => /* @__PURE__ */ jsxs(
|
|
3370
|
+
"div",
|
|
3371
|
+
{
|
|
3372
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3373
|
+
style: { paddingLeft: "8px" },
|
|
3374
|
+
onClick: () => setSelectedPath(file.path),
|
|
3375
|
+
children: [
|
|
3376
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: file.path }),
|
|
3377
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3378
|
+
"button",
|
|
3379
|
+
{
|
|
3380
|
+
onClick: (e) => {
|
|
3381
|
+
e.stopPropagation();
|
|
3382
|
+
removeFile(file.path);
|
|
3383
|
+
},
|
|
3384
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3385
|
+
children: "\xD7"
|
|
3386
|
+
}
|
|
3387
|
+
)
|
|
3388
|
+
]
|
|
3389
|
+
},
|
|
3390
|
+
file.path
|
|
3391
|
+
)),
|
|
3392
|
+
tree.rootDirs.map((node) => renderTreeNode(node, 0)),
|
|
3393
|
+
files.length === 0 && /* @__PURE__ */ 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.` })
|
|
3394
|
+
] })
|
|
3395
|
+
] }),
|
|
3396
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-border rounded-md overflow-hidden", children: activeFile ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col", children: [
|
|
3397
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: activeFile.path }),
|
|
3398
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex-1 animate-pulse bg-muted/50" }), children: /* @__PURE__ */ jsx(
|
|
3399
|
+
CodeEditor,
|
|
3400
|
+
{
|
|
3401
|
+
value: activeFile.content,
|
|
3402
|
+
onChange: handleEditorChange,
|
|
3403
|
+
filename: activeFile.path
|
|
3404
|
+
}
|
|
3405
|
+
) })
|
|
3406
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
|
|
3407
|
+
"Select a file to ",
|
|
3408
|
+
readOnly ? "view" : "edit"
|
|
3409
|
+
] }) })
|
|
3196
3410
|
] })
|
|
3197
3411
|
] });
|
|
3198
3412
|
}
|
|
3413
|
+
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3414
|
+
const client = useAgentPlaneClient();
|
|
3415
|
+
const initialFiles = useMemo(
|
|
3416
|
+
() => initialSkills.flatMap(
|
|
3417
|
+
(s) => s.files.map((f) => ({
|
|
3418
|
+
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3419
|
+
content: f.content
|
|
3420
|
+
}))
|
|
3421
|
+
),
|
|
3422
|
+
[initialSkills]
|
|
3423
|
+
);
|
|
3424
|
+
const handleSave = useCallback(async (files) => {
|
|
3425
|
+
const folderMap = /* @__PURE__ */ new Map();
|
|
3426
|
+
for (const file of files) {
|
|
3427
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3428
|
+
if (slashIdx === -1) {
|
|
3429
|
+
const existing = folderMap.get("(root)") ?? [];
|
|
3430
|
+
existing.push({ path: file.path, content: file.content });
|
|
3431
|
+
folderMap.set("(root)", existing);
|
|
3432
|
+
} else {
|
|
3433
|
+
const folder = file.path.slice(0, slashIdx);
|
|
3434
|
+
const fileName = file.path.slice(slashIdx + 1);
|
|
3435
|
+
const existing = folderMap.get(folder) ?? [];
|
|
3436
|
+
existing.push({ path: fileName, content: file.content });
|
|
3437
|
+
folderMap.set(folder, existing);
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3441
|
+
await client.agents.update(agentId, { skills });
|
|
3442
|
+
onSaved?.();
|
|
3443
|
+
}, [agentId, client, onSaved]);
|
|
3444
|
+
return /* @__PURE__ */ jsx(
|
|
3445
|
+
FileTreeEditor,
|
|
3446
|
+
{
|
|
3447
|
+
initialFiles,
|
|
3448
|
+
onSave: handleSave,
|
|
3449
|
+
title: "Skills",
|
|
3450
|
+
saveLabel: "Save Skills",
|
|
3451
|
+
addFolderLabel: "Skill",
|
|
3452
|
+
newFileTemplate: {
|
|
3453
|
+
filename: "SKILL.md",
|
|
3454
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
);
|
|
3458
|
+
}
|
|
3199
3459
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3200
3460
|
const client = useAgentPlaneClient();
|
|
3201
3461
|
const [plugins, setPlugins] = useState(initialPlugins);
|