@getcatalystiq/agent-plane-ui 0.1.4 → 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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import React3__default, { createContext, useRef, useMemo, useContext, useState, useCallback, useEffect } from 'react';
2
+ import React3__default, { createContext, lazy, useRef, useMemo, useContext, useState, useCallback, useEffect, Suspense } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import useSWR, { useSWRConfig } from 'swr';
5
5
  import { clsx } from 'clsx';
@@ -3073,129 +3073,389 @@ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAl
3073
3073
  )
3074
3074
  ] });
3075
3075
  }
3076
- function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3077
- const client = useAgentPlaneClient();
3078
- const initialFiles = useMemo(
3079
- () => initialSkills.flatMap(
3080
- (s) => s.files.map((f) => ({
3081
- path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
3082
- content: f.content
3083
- }))
3084
- ),
3085
- [initialSkills]
3086
- );
3076
+ var CodeEditor = lazy(() => import('./code-editor-E7L6Y3LM.js'));
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
+ }) {
3087
3147
  const [files, setFiles] = useState(initialFiles);
3088
- const [selectedPath, setSelectedPath] = useState(files[0]?.path ?? null);
3148
+ const [selectedPath, setSelectedPath] = useState(
3149
+ initialFiles.length > 0 ? initialFiles[0].path : null
3150
+ );
3089
3151
  const [saving, setSaving] = useState(false);
3090
- const [addingFile, setAddingFile] = useState(false);
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);
3091
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]);
3092
3180
  const isDirty = useMemo(
3093
- () => JSON.stringify(files) !== JSON.stringify(initialFiles),
3094
- [files, initialFiles]
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]
3095
3188
  );
3096
- const selectedFile = files.find((f) => f.path === selectedPath);
3097
- function updateFileContent(path, content) {
3098
- setFiles((prev) => prev.map((f) => f.path === path ? { ...f, content } : f));
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);
3220
+ }
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);
3099
3226
  }
3100
- function addFile() {
3227
+ function addFileInDir(dirPath) {
3101
3228
  const name = newFileName.trim();
3102
- if (!name || files.some((f) => f.path === name)) return;
3103
- const content = name.endsWith(".md") ? "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n" : "";
3104
- setFiles((prev) => [...prev, { path: name, content }]);
3105
- setSelectedPath(name);
3106
- setAddingFile(false);
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);
3107
3234
  setNewFileName("");
3235
+ setAddingFileInDir(null);
3108
3236
  }
3109
- function removeFile(path) {
3110
- setFiles((prev) => prev.filter((f) => f.path !== path));
3111
- if (selectedPath === path) {
3112
- setSelectedPath(files.find((f) => f.path !== path)?.path ?? null);
3113
- }
3114
- }
3115
- const handleSave = useCallback(async () => {
3237
+ async function handleSave() {
3116
3238
  setSaving(true);
3117
3239
  try {
3118
- const folderMap = /* @__PURE__ */ new Map();
3119
- for (const file of files) {
3120
- const slashIdx = file.path.lastIndexOf("/");
3121
- if (slashIdx === -1) {
3122
- const existing = folderMap.get("(root)") ?? [];
3123
- existing.push({ path: file.path, content: file.content });
3124
- folderMap.set("(root)", existing);
3125
- } else {
3126
- const folder = file.path.slice(0, slashIdx);
3127
- const fileName = file.path.slice(slashIdx + 1);
3128
- const existing = folderMap.get(folder) ?? [];
3129
- existing.push({ path: fileName, content: file.content });
3130
- folderMap.set(folder, existing);
3131
- }
3132
- }
3133
- const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
3134
- await client.agents.update(agentId, { skills });
3135
- onSaved?.();
3240
+ await onSave(files);
3136
3241
  } finally {
3137
3242
  setSaving(false);
3138
3243
  }
3139
- }, [files, agentId, client, onSaved]);
3140
- return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3141
- /* @__PURE__ */ jsx(SectionHeader, { title: "Skills", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3142
- isDirty && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved" }),
3143
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => setAddingFile(true), children: "Add File" }),
3144
- /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || !isDirty, children: saving ? "Saving..." : "Save Skills" })
3145
- ] }) }),
3146
- addingFile && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
3147
- /* @__PURE__ */ jsx(
3148
- Input,
3149
- {
3150
- value: newFileName,
3151
- onChange: (e) => setNewFileName(e.target.value),
3152
- placeholder: "folder/SKILL.md",
3153
- className: "max-w-xs text-sm",
3154
- onKeyDown: (e) => e.key === "Enter" && addFile()
3155
- }
3156
- ),
3157
- /* @__PURE__ */ jsx(Button, { size: "sm", onClick: addFile, children: "Add" }),
3158
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
3159
- setAddingFile(false);
3160
- setNewFileName("");
3161
- }, children: "Cancel" })
3162
- ] }),
3163
- 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: [
3164
- /* @__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(
3165
3249
  "div",
3166
3250
  {
3167
- className: `flex items-center justify-between group rounded px-2 py-1 text-xs cursor-pointer ${selectedPath === f.path ? "bg-accent text-accent-foreground" : "hover:bg-muted/50"}`,
3168
- onClick: () => setSelectedPath(f.path),
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),
3169
3254
  children: [
3170
- /* @__PURE__ */ jsx("span", { className: "truncate", children: f.path }),
3171
- /* @__PURE__ */ jsx(
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(
3172
3261
  "button",
3173
3262
  {
3174
- type: "button",
3175
3263
  onClick: (e) => {
3176
3264
  e.stopPropagation();
3177
- removeFile(f.path);
3265
+ removeDir(node.fullPath);
3178
3266
  },
3179
- className: "opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-destructive ml-1",
3267
+ className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
3180
3268
  children: "\xD7"
3181
3269
  }
3182
3270
  )
3183
3271
  ]
3184
- },
3185
- f.path
3186
- )) }),
3187
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: selectedFile ? /* @__PURE__ */ jsx(
3188
- Textarea,
3189
- {
3190
- value: selectedFile.content,
3191
- onChange: (e) => updateFileContent(selectedFile.path, e.target.value),
3192
- className: "h-full min-h-[300px] font-mono text-xs resize-y",
3193
- placeholder: "Enter skill content..."
3194
3272
  }
3195
- ) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select a file to edit" }) })
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);
@@ -3351,141 +3611,6 @@ function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
3351
3611
  ] })
3352
3612
  ] });
3353
3613
  }
3354
- var FREQUENCIES = [
3355
- { value: "manual", label: "Manual (no schedule)" },
3356
- { value: "hourly", label: "Hourly" },
3357
- { value: "daily", label: "Daily" },
3358
- { value: "weekdays", label: "Weekdays (Mon-Fri)" },
3359
- { value: "weekly", label: "Weekly" }
3360
- ];
3361
- var DAYS_OF_WEEK = [
3362
- { value: 0, label: "Sunday" },
3363
- { value: 1, label: "Monday" },
3364
- { value: 2, label: "Tuesday" },
3365
- { value: 3, label: "Wednesday" },
3366
- { value: 4, label: "Thursday" },
3367
- { value: 5, label: "Friday" },
3368
- { value: 6, label: "Saturday" }
3369
- ];
3370
- function formatTimeForInput(time) {
3371
- if (!time) return "09:00";
3372
- return time.slice(0, 5);
3373
- }
3374
- function AgentScheduleForm({ initialSchedules, timezone }) {
3375
- return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3376
- /* @__PURE__ */ jsx(SectionHeader, { title: "Schedules", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3377
- "Timezone: ",
3378
- timezone
3379
- ] }) }),
3380
- initialSchedules.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No schedules configured." }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: initialSchedules.map((schedule) => /* @__PURE__ */ jsx(
3381
- ScheduleCard,
3382
- {
3383
- schedule,
3384
- timezone
3385
- },
3386
- schedule.id
3387
- )) })
3388
- ] });
3389
- }
3390
- function ScheduleCard({
3391
- schedule,
3392
- timezone
3393
- }) {
3394
- const [frequency, setFrequency] = useState(schedule.frequency);
3395
- const [time, setTime] = useState(formatTimeForInput(schedule.time));
3396
- const [dayOfWeek, setDayOfWeek] = useState(schedule.day_of_week ?? 1);
3397
- const [prompt, setPrompt] = useState(schedule.prompt ?? "");
3398
- const [enabled, setEnabled] = useState(schedule.enabled);
3399
- const [name, setName] = useState(schedule.name ?? "");
3400
- const showTimePicker = ["daily", "weekdays", "weekly"].includes(frequency);
3401
- const showDayPicker = frequency === "weekly";
3402
- const canEnable = frequency !== "manual";
3403
- return /* @__PURE__ */ jsxs("div", { className: "rounded border border-muted-foreground/15 p-4 space-y-3", children: [
3404
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
3405
- /* @__PURE__ */ jsx(
3406
- Input,
3407
- {
3408
- value: name,
3409
- onChange: (e) => setName(e.target.value),
3410
- placeholder: "Schedule name (optional)",
3411
- className: "max-w-xs text-sm"
3412
- }
3413
- ),
3414
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: canEnable && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
3415
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: enabled ? "Enabled" : "Disabled" }),
3416
- /* @__PURE__ */ jsx(
3417
- "button",
3418
- {
3419
- type: "button",
3420
- role: "switch",
3421
- "aria-checked": enabled,
3422
- onClick: () => setEnabled(!enabled),
3423
- className: `relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${enabled ? "bg-primary" : "bg-muted"}`,
3424
- children: /* @__PURE__ */ jsx(
3425
- "span",
3426
- {
3427
- className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-background shadow-lg ring-0 transition-transform ${enabled ? "translate-x-4" : "translate-x-0"}`
3428
- }
3429
- )
3430
- }
3431
- )
3432
- ] }) })
3433
- ] }),
3434
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
3435
- /* @__PURE__ */ jsx(FormField, { label: "Frequency", children: /* @__PURE__ */ jsx(
3436
- Select,
3437
- {
3438
- value: frequency,
3439
- onChange: (e) => {
3440
- const newFreq = e.target.value;
3441
- setFrequency(newFreq);
3442
- if (newFreq === "manual") setEnabled(false);
3443
- else setEnabled(true);
3444
- },
3445
- children: FREQUENCIES.map((f) => /* @__PURE__ */ jsx("option", { value: f.value, children: f.label }, f.value))
3446
- }
3447
- ) }),
3448
- showTimePicker && /* @__PURE__ */ jsx(FormField, { label: `Time (${timezone})`, children: /* @__PURE__ */ jsx(
3449
- Input,
3450
- {
3451
- type: "time",
3452
- value: time,
3453
- onChange: (e) => setTime(e.target.value)
3454
- }
3455
- ) }),
3456
- showDayPicker && /* @__PURE__ */ jsx(FormField, { label: "Day of Week", children: /* @__PURE__ */ jsx(
3457
- Select,
3458
- {
3459
- value: dayOfWeek.toString(),
3460
- onChange: (e) => setDayOfWeek(parseInt(e.target.value)),
3461
- children: DAYS_OF_WEEK.map((d) => /* @__PURE__ */ jsx("option", { value: d.value, children: d.label }, d.value))
3462
- }
3463
- ) })
3464
- ] }),
3465
- frequency !== "manual" && /* @__PURE__ */ jsx(FormField, { label: "Prompt", children: /* @__PURE__ */ jsx(
3466
- Textarea,
3467
- {
3468
- value: prompt,
3469
- onChange: (e) => setPrompt(e.target.value),
3470
- rows: 3,
3471
- placeholder: "Enter the prompt to send on each scheduled run...",
3472
- className: "resize-y min-h-[60px]"
3473
- }
3474
- ) }),
3475
- (schedule.last_run_at || schedule.next_run_at) && /* @__PURE__ */ jsxs("div", { className: "flex gap-6 text-sm text-muted-foreground pt-1", children: [
3476
- schedule.last_run_at && /* @__PURE__ */ jsxs("div", { children: [
3477
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Last run:" }),
3478
- " ",
3479
- /* @__PURE__ */ jsx(LocalDate, { value: schedule.last_run_at })
3480
- ] }),
3481
- schedule.next_run_at && /* @__PURE__ */ jsxs("div", { children: [
3482
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Next run:" }),
3483
- " ",
3484
- /* @__PURE__ */ jsx(LocalDate, { value: schedule.next_run_at })
3485
- ] })
3486
- ] })
3487
- ] });
3488
- }
3489
3614
  function AgentRuns({ agentId }) {
3490
3615
  const { LinkComponent, basePath } = useNavigation();
3491
3616
  const { data, error, isLoading } = useApi(
@@ -3725,7 +3850,7 @@ function AgentA2aInfo({
3725
3850
  ] })
3726
3851
  ] });
3727
3852
  }
3728
- function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, timezone = "UTC" }) {
3853
+ function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug }) {
3729
3854
  const { LinkComponent, basePath } = useNavigation();
3730
3855
  const { mutate } = useSWRConfig();
3731
3856
  const cacheKey = `agent-${agentId}`;
@@ -3828,16 +3953,6 @@ function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, timezone = "UTC" })
3828
3953
  }
3829
3954
  )
3830
3955
  },
3831
- {
3832
- label: "Schedules",
3833
- content: /* @__PURE__ */ jsx(
3834
- AgentScheduleForm,
3835
- {
3836
- initialSchedules: [],
3837
- timezone
3838
- }
3839
- )
3840
- },
3841
3956
  {
3842
3957
  label: "Runs",
3843
3958
  content: /* @__PURE__ */ jsx(AgentRuns, { agentId: agent.id })
@@ -3847,5 +3962,140 @@ function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, timezone = "UTC" })
3847
3962
  )
3848
3963
  ] });
3849
3964
  }
3965
+ var FREQUENCIES = [
3966
+ { value: "manual", label: "Manual (no schedule)" },
3967
+ { value: "hourly", label: "Hourly" },
3968
+ { value: "daily", label: "Daily" },
3969
+ { value: "weekdays", label: "Weekdays (Mon-Fri)" },
3970
+ { value: "weekly", label: "Weekly" }
3971
+ ];
3972
+ var DAYS_OF_WEEK = [
3973
+ { value: 0, label: "Sunday" },
3974
+ { value: 1, label: "Monday" },
3975
+ { value: 2, label: "Tuesday" },
3976
+ { value: 3, label: "Wednesday" },
3977
+ { value: 4, label: "Thursday" },
3978
+ { value: 5, label: "Friday" },
3979
+ { value: 6, label: "Saturday" }
3980
+ ];
3981
+ function formatTimeForInput(time) {
3982
+ if (!time) return "09:00";
3983
+ return time.slice(0, 5);
3984
+ }
3985
+ function AgentScheduleForm({ initialSchedules, timezone }) {
3986
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3987
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Schedules", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3988
+ "Timezone: ",
3989
+ timezone
3990
+ ] }) }),
3991
+ initialSchedules.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No schedules configured." }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: initialSchedules.map((schedule) => /* @__PURE__ */ jsx(
3992
+ ScheduleCard,
3993
+ {
3994
+ schedule,
3995
+ timezone
3996
+ },
3997
+ schedule.id
3998
+ )) })
3999
+ ] });
4000
+ }
4001
+ function ScheduleCard({
4002
+ schedule,
4003
+ timezone
4004
+ }) {
4005
+ const [frequency, setFrequency] = useState(schedule.frequency);
4006
+ const [time, setTime] = useState(formatTimeForInput(schedule.time));
4007
+ const [dayOfWeek, setDayOfWeek] = useState(schedule.day_of_week ?? 1);
4008
+ const [prompt, setPrompt] = useState(schedule.prompt ?? "");
4009
+ const [enabled, setEnabled] = useState(schedule.enabled);
4010
+ const [name, setName] = useState(schedule.name ?? "");
4011
+ const showTimePicker = ["daily", "weekdays", "weekly"].includes(frequency);
4012
+ const showDayPicker = frequency === "weekly";
4013
+ const canEnable = frequency !== "manual";
4014
+ return /* @__PURE__ */ jsxs("div", { className: "rounded border border-muted-foreground/15 p-4 space-y-3", children: [
4015
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
4016
+ /* @__PURE__ */ jsx(
4017
+ Input,
4018
+ {
4019
+ value: name,
4020
+ onChange: (e) => setName(e.target.value),
4021
+ placeholder: "Schedule name (optional)",
4022
+ className: "max-w-xs text-sm"
4023
+ }
4024
+ ),
4025
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: canEnable && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
4026
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: enabled ? "Enabled" : "Disabled" }),
4027
+ /* @__PURE__ */ jsx(
4028
+ "button",
4029
+ {
4030
+ type: "button",
4031
+ role: "switch",
4032
+ "aria-checked": enabled,
4033
+ onClick: () => setEnabled(!enabled),
4034
+ className: `relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${enabled ? "bg-primary" : "bg-muted"}`,
4035
+ children: /* @__PURE__ */ jsx(
4036
+ "span",
4037
+ {
4038
+ className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-background shadow-lg ring-0 transition-transform ${enabled ? "translate-x-4" : "translate-x-0"}`
4039
+ }
4040
+ )
4041
+ }
4042
+ )
4043
+ ] }) })
4044
+ ] }),
4045
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
4046
+ /* @__PURE__ */ jsx(FormField, { label: "Frequency", children: /* @__PURE__ */ jsx(
4047
+ Select,
4048
+ {
4049
+ value: frequency,
4050
+ onChange: (e) => {
4051
+ const newFreq = e.target.value;
4052
+ setFrequency(newFreq);
4053
+ if (newFreq === "manual") setEnabled(false);
4054
+ else setEnabled(true);
4055
+ },
4056
+ children: FREQUENCIES.map((f) => /* @__PURE__ */ jsx("option", { value: f.value, children: f.label }, f.value))
4057
+ }
4058
+ ) }),
4059
+ showTimePicker && /* @__PURE__ */ jsx(FormField, { label: `Time (${timezone})`, children: /* @__PURE__ */ jsx(
4060
+ Input,
4061
+ {
4062
+ type: "time",
4063
+ value: time,
4064
+ onChange: (e) => setTime(e.target.value)
4065
+ }
4066
+ ) }),
4067
+ showDayPicker && /* @__PURE__ */ jsx(FormField, { label: "Day of Week", children: /* @__PURE__ */ jsx(
4068
+ Select,
4069
+ {
4070
+ value: dayOfWeek.toString(),
4071
+ onChange: (e) => setDayOfWeek(parseInt(e.target.value)),
4072
+ children: DAYS_OF_WEEK.map((d) => /* @__PURE__ */ jsx("option", { value: d.value, children: d.label }, d.value))
4073
+ }
4074
+ ) })
4075
+ ] }),
4076
+ frequency !== "manual" && /* @__PURE__ */ jsx(FormField, { label: "Prompt", children: /* @__PURE__ */ jsx(
4077
+ Textarea,
4078
+ {
4079
+ value: prompt,
4080
+ onChange: (e) => setPrompt(e.target.value),
4081
+ rows: 3,
4082
+ placeholder: "Enter the prompt to send on each scheduled run...",
4083
+ className: "resize-y min-h-[60px]"
4084
+ }
4085
+ ) }),
4086
+ (schedule.last_run_at || schedule.next_run_at) && /* @__PURE__ */ jsxs("div", { className: "flex gap-6 text-sm text-muted-foreground pt-1", children: [
4087
+ schedule.last_run_at && /* @__PURE__ */ jsxs("div", { children: [
4088
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Last run:" }),
4089
+ " ",
4090
+ /* @__PURE__ */ jsx(LocalDate, { value: schedule.last_run_at })
4091
+ ] }),
4092
+ schedule.next_run_at && /* @__PURE__ */ jsxs("div", { children: [
4093
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Next run:" }),
4094
+ " ",
4095
+ /* @__PURE__ */ jsx(LocalDate, { value: schedule.next_run_at })
4096
+ ] })
4097
+ ] })
4098
+ ] });
4099
+ }
3850
4100
 
3851
4101
  export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPlaneProvider, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Skeleton, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };