@getcatalystiq/agent-plane-ui 0.1.28 → 0.1.30
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 +210 -17
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +210 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3362,7 +3362,8 @@ function FileTreeEditor2({
|
|
|
3362
3362
|
addFolderLabel = "Folder",
|
|
3363
3363
|
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" },
|
|
3364
3364
|
savedVersion,
|
|
3365
|
-
fixedStructure = false
|
|
3365
|
+
fixedStructure = false,
|
|
3366
|
+
headerActions
|
|
3366
3367
|
}) {
|
|
3367
3368
|
const [files, setFiles] = React.useState(initialFiles);
|
|
3368
3369
|
const [selectedPath, setSelectedPath] = React.useState(
|
|
@@ -3553,7 +3554,10 @@ function FileTreeEditor2({
|
|
|
3553
3554
|
isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3554
3555
|
readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3555
3556
|
] }),
|
|
3556
|
-
|
|
3557
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3558
|
+
headerActions,
|
|
3559
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3560
|
+
] })
|
|
3557
3561
|
] }),
|
|
3558
3562
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3559
3563
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
@@ -3630,17 +3634,193 @@ function FileTreeEditor2({
|
|
|
3630
3634
|
] })
|
|
3631
3635
|
] });
|
|
3632
3636
|
}
|
|
3637
|
+
function TabButton({ label, active, onClick }) {
|
|
3638
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3639
|
+
"button",
|
|
3640
|
+
{
|
|
3641
|
+
onClick,
|
|
3642
|
+
className: `relative pb-2 text-sm font-medium transition-colors ${active ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
3643
|
+
children: [
|
|
3644
|
+
label,
|
|
3645
|
+
active && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground rounded-full" })
|
|
3646
|
+
]
|
|
3647
|
+
}
|
|
3648
|
+
);
|
|
3649
|
+
}
|
|
3650
|
+
function ImportSkillDialog({ open, onOpenChange, onImported, existingFolders }) {
|
|
3651
|
+
const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
|
|
3652
|
+
const [tab, setTab] = React.useState("all");
|
|
3653
|
+
const [entries, setEntries] = React.useState([]);
|
|
3654
|
+
const [loading, setLoading] = React.useState(false);
|
|
3655
|
+
const [error, setError] = React.useState("");
|
|
3656
|
+
const [search, setSearch] = React.useState("");
|
|
3657
|
+
const [selected, setSelected] = React.useState(null);
|
|
3658
|
+
const [preview, setPreview] = React.useState("");
|
|
3659
|
+
const [previewLoading, setPreviewLoading] = React.useState(false);
|
|
3660
|
+
const [importing, setImporting] = React.useState(false);
|
|
3661
|
+
const [importError, setImportError] = React.useState("");
|
|
3662
|
+
const [url, setUrl] = React.useState("");
|
|
3663
|
+
const [urlImporting, setUrlImporting] = React.useState(false);
|
|
3664
|
+
React.useEffect(() => {
|
|
3665
|
+
if (!open) return;
|
|
3666
|
+
setLoading(true);
|
|
3667
|
+
setError("");
|
|
3668
|
+
setSelected(null);
|
|
3669
|
+
setPreview("");
|
|
3670
|
+
client.skillsDirectory.list(tab).then((data) => setEntries(data)).catch((err) => setError(err instanceof Error ? err.message : "Failed to load skills")).finally(() => setLoading(false));
|
|
3671
|
+
}, [tab, open, client]);
|
|
3672
|
+
const filtered = React.useMemo(() => {
|
|
3673
|
+
if (!search.trim()) return entries;
|
|
3674
|
+
const q = search.toLowerCase();
|
|
3675
|
+
return entries.filter(
|
|
3676
|
+
(e) => e.name.toLowerCase().includes(q) || e.owner.toLowerCase().includes(q) || e.repo.toLowerCase().includes(q)
|
|
3677
|
+
);
|
|
3678
|
+
}, [entries, search]);
|
|
3679
|
+
async function handleSelect(entry) {
|
|
3680
|
+
setSelected(entry);
|
|
3681
|
+
setPreviewLoading(true);
|
|
3682
|
+
setPreview("");
|
|
3683
|
+
setImportError("");
|
|
3684
|
+
try {
|
|
3685
|
+
const content = await client.skillsDirectory.preview(entry.owner, entry.repo, entry.skill);
|
|
3686
|
+
setPreview(content);
|
|
3687
|
+
} catch (err) {
|
|
3688
|
+
setPreview(`Failed to load preview: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
3689
|
+
} finally {
|
|
3690
|
+
setPreviewLoading(false);
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
async function handleImport() {
|
|
3694
|
+
if (!selected) return;
|
|
3695
|
+
setImporting(true);
|
|
3696
|
+
setImportError("");
|
|
3697
|
+
try {
|
|
3698
|
+
const result = await client.skillsDirectory.import({
|
|
3699
|
+
owner: selected.owner,
|
|
3700
|
+
repo: selected.repo,
|
|
3701
|
+
skill_name: selected.skill
|
|
3702
|
+
});
|
|
3703
|
+
if (existingFolders.includes(result.folder)) {
|
|
3704
|
+
setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
|
|
3705
|
+
return;
|
|
3706
|
+
}
|
|
3707
|
+
if (result.warnings.length > 0) {
|
|
3708
|
+
setImportError(`Imported with warnings: ${result.warnings.join("; ")}`);
|
|
3709
|
+
}
|
|
3710
|
+
onImported({ folder: result.folder, files: result.files });
|
|
3711
|
+
onOpenChange(false);
|
|
3712
|
+
resetState();
|
|
3713
|
+
} catch (err) {
|
|
3714
|
+
setImportError(err instanceof Error ? err.message : "Failed to import skill");
|
|
3715
|
+
} finally {
|
|
3716
|
+
setImporting(false);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
async function handleUrlImport() {
|
|
3720
|
+
if (!url.trim()) return;
|
|
3721
|
+
setUrlImporting(true);
|
|
3722
|
+
setImportError("");
|
|
3723
|
+
try {
|
|
3724
|
+
const result = await client.skillsDirectory.import({ url: url.trim() });
|
|
3725
|
+
if (existingFolders.includes(result.folder)) {
|
|
3726
|
+
setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
onImported({ folder: result.folder, files: result.files });
|
|
3730
|
+
onOpenChange(false);
|
|
3731
|
+
resetState();
|
|
3732
|
+
} catch (err) {
|
|
3733
|
+
setImportError(err instanceof Error ? err.message : "Failed to import skill");
|
|
3734
|
+
} finally {
|
|
3735
|
+
setUrlImporting(false);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
function resetState() {
|
|
3739
|
+
setTab("all");
|
|
3740
|
+
setSearch("");
|
|
3741
|
+
setSelected(null);
|
|
3742
|
+
setPreview("");
|
|
3743
|
+
setUrl("");
|
|
3744
|
+
setError("");
|
|
3745
|
+
setImportError("");
|
|
3746
|
+
}
|
|
3747
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Dialog, { open, onOpenChange: (v) => {
|
|
3748
|
+
onOpenChange(v);
|
|
3749
|
+
if (!v) resetState();
|
|
3750
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
|
|
3751
|
+
/* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogHeader, { children: [
|
|
3752
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.DialogTitle, { children: "Import from skills.sh" }),
|
|
3753
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.DialogDescription, { children: "Browse the open skills directory and import skills into this agent." })
|
|
3754
|
+
] }),
|
|
3755
|
+
/* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogBody, { className: "flex-1 overflow-hidden flex flex-col gap-4", children: [
|
|
3756
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
|
|
3757
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "All Time", active: tab === "all", onClick: () => setTab("all") }),
|
|
3758
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Trending", active: tab === "trending", onClick: () => setTab("trending") }),
|
|
3759
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Hot", active: tab === "hot", onClick: () => setTab("hot") })
|
|
3760
|
+
] }),
|
|
3761
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Input, { placeholder: "Search skills...", value: search, onChange: (e) => setSearch(e.target.value) }),
|
|
3762
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
3763
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden flex gap-4 min-h-0", children: [
|
|
3764
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1/2 overflow-y-auto border border-muted-foreground/25 rounded-lg", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-sm text-muted-foreground", children: "Loading skills..." }) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-sm text-muted-foreground", children: search ? "No skills match your search" : "No skills found" }) : filtered.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3765
|
+
"button",
|
|
3766
|
+
{
|
|
3767
|
+
onClick: () => handleSelect(entry),
|
|
3768
|
+
className: `w-full text-left px-3 py-2 border-b border-muted-foreground/10 hover:bg-muted/50 transition-colors ${selected?.skill === entry.skill && selected?.owner === entry.owner ? "bg-muted/50" : ""}`,
|
|
3769
|
+
children: [
|
|
3770
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-sm truncate", children: entry.name }),
|
|
3771
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3772
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
|
|
3773
|
+
entry.owner,
|
|
3774
|
+
"/",
|
|
3775
|
+
entry.repo
|
|
3776
|
+
] }),
|
|
3777
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-mono ml-2 shrink-0", children: entry.installs })
|
|
3778
|
+
] })
|
|
3779
|
+
]
|
|
3780
|
+
},
|
|
3781
|
+
`${entry.owner}/${entry.repo}/${entry.skill}`
|
|
3782
|
+
)) }),
|
|
3783
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1/2 overflow-y-auto border border-muted-foreground/25 rounded-lg p-3", children: !selected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground", children: "Select a skill to preview" }) : previewLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground", children: "Loading preview..." }) : /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs whitespace-pre-wrap break-words font-mono", children: preview }) })
|
|
3784
|
+
] }),
|
|
3785
|
+
importError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: importError }),
|
|
3786
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-muted-foreground/25 pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.FormField, { label: "Or paste a skills.sh URL", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3787
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3788
|
+
chunkXXF4U7WL_cjs.Input,
|
|
3789
|
+
{
|
|
3790
|
+
value: url,
|
|
3791
|
+
onChange: (e) => setUrl(e.target.value),
|
|
3792
|
+
placeholder: "skills.sh/owner/repo/skill",
|
|
3793
|
+
onKeyDown: (e) => e.key === "Enter" && handleUrlImport(),
|
|
3794
|
+
className: "flex-1"
|
|
3795
|
+
}
|
|
3796
|
+
),
|
|
3797
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", variant: "outline", onClick: handleUrlImport, disabled: urlImporting || !url.trim(), children: urlImporting ? "Importing..." : "Import URL" })
|
|
3798
|
+
] }) }) })
|
|
3799
|
+
] }),
|
|
3800
|
+
/* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogFooter, { children: [
|
|
3801
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3802
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", onClick: handleImport, disabled: !selected || importing || previewLoading, children: importing ? "Importing..." : "Import Selected" })
|
|
3803
|
+
] })
|
|
3804
|
+
] }) });
|
|
3805
|
+
}
|
|
3633
3806
|
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3634
3807
|
const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
|
|
3808
|
+
const [importOpen, setImportOpen] = React.useState(false);
|
|
3809
|
+
const [extraSkills, setExtraSkills] = React.useState([]);
|
|
3810
|
+
const allSkills = React.useMemo(() => [...initialSkills, ...extraSkills], [initialSkills, extraSkills]);
|
|
3635
3811
|
const initialFiles = React.useMemo(
|
|
3636
|
-
() =>
|
|
3812
|
+
() => allSkills.flatMap(
|
|
3637
3813
|
(s) => s.files.map((f) => ({
|
|
3638
3814
|
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3639
3815
|
content: f.content
|
|
3640
3816
|
}))
|
|
3641
3817
|
),
|
|
3642
|
-
[
|
|
3818
|
+
[allSkills]
|
|
3643
3819
|
);
|
|
3820
|
+
const existingFolders = React.useMemo(() => allSkills.map((s) => s.folder), [allSkills]);
|
|
3821
|
+
const handleImported = React.useCallback((skill) => {
|
|
3822
|
+
setExtraSkills((prev) => [...prev, skill]);
|
|
3823
|
+
}, []);
|
|
3644
3824
|
const handleSave = React.useCallback(async (files) => {
|
|
3645
3825
|
const folderMap = /* @__PURE__ */ new Map();
|
|
3646
3826
|
for (const file of files) {
|
|
@@ -3659,22 +3839,35 @@ function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
|
3659
3839
|
}
|
|
3660
3840
|
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3661
3841
|
await client.agents.update(agentId, { skills });
|
|
3842
|
+
setExtraSkills([]);
|
|
3662
3843
|
onSaved?.();
|
|
3663
3844
|
}, [agentId, client, onSaved]);
|
|
3664
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3845
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3846
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3847
|
+
FileTreeEditor2,
|
|
3848
|
+
{
|
|
3849
|
+
initialFiles,
|
|
3850
|
+
onSave: handleSave,
|
|
3851
|
+
title: "Skills",
|
|
3852
|
+
saveLabel: "Save Skills",
|
|
3853
|
+
addFolderLabel: "Skill",
|
|
3854
|
+
newFileTemplate: {
|
|
3855
|
+
filename: "SKILL.md",
|
|
3856
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3857
|
+
},
|
|
3858
|
+
headerActions: /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", variant: "outline", onClick: () => setImportOpen(true), children: "Import from skills.sh" })
|
|
3675
3859
|
}
|
|
3676
|
-
|
|
3677
|
-
|
|
3860
|
+
),
|
|
3861
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3862
|
+
ImportSkillDialog,
|
|
3863
|
+
{
|
|
3864
|
+
open: importOpen,
|
|
3865
|
+
onOpenChange: setImportOpen,
|
|
3866
|
+
onImported: handleImported,
|
|
3867
|
+
existingFolders
|
|
3868
|
+
}
|
|
3869
|
+
)
|
|
3870
|
+
] });
|
|
3678
3871
|
}
|
|
3679
3872
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3680
3873
|
const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
|
package/dist/index.d.cts
CHANGED
|
@@ -7,6 +7,22 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
8
|
import { DailyAgentStat } from './charts.cjs';
|
|
9
9
|
|
|
10
|
+
/** Skills directory types. */
|
|
11
|
+
interface SkillDirectoryEntry {
|
|
12
|
+
name: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
skill: string;
|
|
16
|
+
installs: string;
|
|
17
|
+
}
|
|
18
|
+
interface ImportedSkillResult {
|
|
19
|
+
folder: string;
|
|
20
|
+
files: Array<{
|
|
21
|
+
path: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}>;
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
10
26
|
/** Minimal stream event types used by the playground UI. */
|
|
11
27
|
interface PlaygroundTextDeltaEvent {
|
|
12
28
|
type: "text_delta";
|
|
@@ -155,6 +171,17 @@ interface AgentPlaneClient {
|
|
|
155
171
|
toolkits(): Promise<unknown[]>;
|
|
156
172
|
tools(toolkit: string): Promise<unknown[]>;
|
|
157
173
|
};
|
|
174
|
+
skillsDirectory: {
|
|
175
|
+
list(tab?: "all" | "trending" | "hot"): Promise<SkillDirectoryEntry[]>;
|
|
176
|
+
preview(owner: string, repo: string, skill: string): Promise<string>;
|
|
177
|
+
import(params: {
|
|
178
|
+
owner: string;
|
|
179
|
+
repo: string;
|
|
180
|
+
skill_name: string;
|
|
181
|
+
} | {
|
|
182
|
+
url: string;
|
|
183
|
+
}): Promise<ImportedSkillResult>;
|
|
184
|
+
};
|
|
158
185
|
pluginMarketplaces: {
|
|
159
186
|
list(): Promise<unknown[]>;
|
|
160
187
|
get(marketplaceId: string): Promise<unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,22 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
8
|
import { DailyAgentStat } from './charts.js';
|
|
9
9
|
|
|
10
|
+
/** Skills directory types. */
|
|
11
|
+
interface SkillDirectoryEntry {
|
|
12
|
+
name: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
skill: string;
|
|
16
|
+
installs: string;
|
|
17
|
+
}
|
|
18
|
+
interface ImportedSkillResult {
|
|
19
|
+
folder: string;
|
|
20
|
+
files: Array<{
|
|
21
|
+
path: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}>;
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
10
26
|
/** Minimal stream event types used by the playground UI. */
|
|
11
27
|
interface PlaygroundTextDeltaEvent {
|
|
12
28
|
type: "text_delta";
|
|
@@ -155,6 +171,17 @@ interface AgentPlaneClient {
|
|
|
155
171
|
toolkits(): Promise<unknown[]>;
|
|
156
172
|
tools(toolkit: string): Promise<unknown[]>;
|
|
157
173
|
};
|
|
174
|
+
skillsDirectory: {
|
|
175
|
+
list(tab?: "all" | "trending" | "hot"): Promise<SkillDirectoryEntry[]>;
|
|
176
|
+
preview(owner: string, repo: string, skill: string): Promise<string>;
|
|
177
|
+
import(params: {
|
|
178
|
+
owner: string;
|
|
179
|
+
repo: string;
|
|
180
|
+
skill_name: string;
|
|
181
|
+
} | {
|
|
182
|
+
url: string;
|
|
183
|
+
}): Promise<ImportedSkillResult>;
|
|
184
|
+
};
|
|
158
185
|
pluginMarketplaces: {
|
|
159
186
|
list(): Promise<unknown[]>;
|
|
160
187
|
get(marketplaceId: string): Promise<unknown>;
|
package/dist/index.js
CHANGED
|
@@ -3338,7 +3338,8 @@ function FileTreeEditor2({
|
|
|
3338
3338
|
addFolderLabel = "Folder",
|
|
3339
3339
|
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" },
|
|
3340
3340
|
savedVersion,
|
|
3341
|
-
fixedStructure = false
|
|
3341
|
+
fixedStructure = false,
|
|
3342
|
+
headerActions
|
|
3342
3343
|
}) {
|
|
3343
3344
|
const [files, setFiles] = useState(initialFiles);
|
|
3344
3345
|
const [selectedPath, setSelectedPath] = useState(
|
|
@@ -3529,7 +3530,10 @@ function FileTreeEditor2({
|
|
|
3529
3530
|
isDirty && !readOnly && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3530
3531
|
readOnly && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3531
3532
|
] }),
|
|
3532
|
-
|
|
3533
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3534
|
+
headerActions,
|
|
3535
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3536
|
+
] })
|
|
3533
3537
|
] }),
|
|
3534
3538
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3535
3539
|
/* @__PURE__ */ jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
@@ -3606,17 +3610,193 @@ function FileTreeEditor2({
|
|
|
3606
3610
|
] })
|
|
3607
3611
|
] });
|
|
3608
3612
|
}
|
|
3613
|
+
function TabButton({ label, active, onClick }) {
|
|
3614
|
+
return /* @__PURE__ */ jsxs(
|
|
3615
|
+
"button",
|
|
3616
|
+
{
|
|
3617
|
+
onClick,
|
|
3618
|
+
className: `relative pb-2 text-sm font-medium transition-colors ${active ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
3619
|
+
children: [
|
|
3620
|
+
label,
|
|
3621
|
+
active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground rounded-full" })
|
|
3622
|
+
]
|
|
3623
|
+
}
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
function ImportSkillDialog({ open, onOpenChange, onImported, existingFolders }) {
|
|
3627
|
+
const client = useAgentPlaneClient();
|
|
3628
|
+
const [tab, setTab] = useState("all");
|
|
3629
|
+
const [entries, setEntries] = useState([]);
|
|
3630
|
+
const [loading, setLoading] = useState(false);
|
|
3631
|
+
const [error, setError] = useState("");
|
|
3632
|
+
const [search, setSearch] = useState("");
|
|
3633
|
+
const [selected, setSelected] = useState(null);
|
|
3634
|
+
const [preview, setPreview] = useState("");
|
|
3635
|
+
const [previewLoading, setPreviewLoading] = useState(false);
|
|
3636
|
+
const [importing, setImporting] = useState(false);
|
|
3637
|
+
const [importError, setImportError] = useState("");
|
|
3638
|
+
const [url, setUrl] = useState("");
|
|
3639
|
+
const [urlImporting, setUrlImporting] = useState(false);
|
|
3640
|
+
useEffect(() => {
|
|
3641
|
+
if (!open) return;
|
|
3642
|
+
setLoading(true);
|
|
3643
|
+
setError("");
|
|
3644
|
+
setSelected(null);
|
|
3645
|
+
setPreview("");
|
|
3646
|
+
client.skillsDirectory.list(tab).then((data) => setEntries(data)).catch((err) => setError(err instanceof Error ? err.message : "Failed to load skills")).finally(() => setLoading(false));
|
|
3647
|
+
}, [tab, open, client]);
|
|
3648
|
+
const filtered = useMemo(() => {
|
|
3649
|
+
if (!search.trim()) return entries;
|
|
3650
|
+
const q = search.toLowerCase();
|
|
3651
|
+
return entries.filter(
|
|
3652
|
+
(e) => e.name.toLowerCase().includes(q) || e.owner.toLowerCase().includes(q) || e.repo.toLowerCase().includes(q)
|
|
3653
|
+
);
|
|
3654
|
+
}, [entries, search]);
|
|
3655
|
+
async function handleSelect(entry) {
|
|
3656
|
+
setSelected(entry);
|
|
3657
|
+
setPreviewLoading(true);
|
|
3658
|
+
setPreview("");
|
|
3659
|
+
setImportError("");
|
|
3660
|
+
try {
|
|
3661
|
+
const content = await client.skillsDirectory.preview(entry.owner, entry.repo, entry.skill);
|
|
3662
|
+
setPreview(content);
|
|
3663
|
+
} catch (err) {
|
|
3664
|
+
setPreview(`Failed to load preview: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
3665
|
+
} finally {
|
|
3666
|
+
setPreviewLoading(false);
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
async function handleImport() {
|
|
3670
|
+
if (!selected) return;
|
|
3671
|
+
setImporting(true);
|
|
3672
|
+
setImportError("");
|
|
3673
|
+
try {
|
|
3674
|
+
const result = await client.skillsDirectory.import({
|
|
3675
|
+
owner: selected.owner,
|
|
3676
|
+
repo: selected.repo,
|
|
3677
|
+
skill_name: selected.skill
|
|
3678
|
+
});
|
|
3679
|
+
if (existingFolders.includes(result.folder)) {
|
|
3680
|
+
setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
if (result.warnings.length > 0) {
|
|
3684
|
+
setImportError(`Imported with warnings: ${result.warnings.join("; ")}`);
|
|
3685
|
+
}
|
|
3686
|
+
onImported({ folder: result.folder, files: result.files });
|
|
3687
|
+
onOpenChange(false);
|
|
3688
|
+
resetState();
|
|
3689
|
+
} catch (err) {
|
|
3690
|
+
setImportError(err instanceof Error ? err.message : "Failed to import skill");
|
|
3691
|
+
} finally {
|
|
3692
|
+
setImporting(false);
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
async function handleUrlImport() {
|
|
3696
|
+
if (!url.trim()) return;
|
|
3697
|
+
setUrlImporting(true);
|
|
3698
|
+
setImportError("");
|
|
3699
|
+
try {
|
|
3700
|
+
const result = await client.skillsDirectory.import({ url: url.trim() });
|
|
3701
|
+
if (existingFolders.includes(result.folder)) {
|
|
3702
|
+
setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
|
|
3703
|
+
return;
|
|
3704
|
+
}
|
|
3705
|
+
onImported({ folder: result.folder, files: result.files });
|
|
3706
|
+
onOpenChange(false);
|
|
3707
|
+
resetState();
|
|
3708
|
+
} catch (err) {
|
|
3709
|
+
setImportError(err instanceof Error ? err.message : "Failed to import skill");
|
|
3710
|
+
} finally {
|
|
3711
|
+
setUrlImporting(false);
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
function resetState() {
|
|
3715
|
+
setTab("all");
|
|
3716
|
+
setSearch("");
|
|
3717
|
+
setSelected(null);
|
|
3718
|
+
setPreview("");
|
|
3719
|
+
setUrl("");
|
|
3720
|
+
setError("");
|
|
3721
|
+
setImportError("");
|
|
3722
|
+
}
|
|
3723
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (v) => {
|
|
3724
|
+
onOpenChange(v);
|
|
3725
|
+
if (!v) resetState();
|
|
3726
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
|
|
3727
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3728
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Import from skills.sh" }),
|
|
3729
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Browse the open skills directory and import skills into this agent." })
|
|
3730
|
+
] }),
|
|
3731
|
+
/* @__PURE__ */ jsxs(DialogBody, { className: "flex-1 overflow-hidden flex flex-col gap-4", children: [
|
|
3732
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
3733
|
+
/* @__PURE__ */ jsx(TabButton, { label: "All Time", active: tab === "all", onClick: () => setTab("all") }),
|
|
3734
|
+
/* @__PURE__ */ jsx(TabButton, { label: "Trending", active: tab === "trending", onClick: () => setTab("trending") }),
|
|
3735
|
+
/* @__PURE__ */ jsx(TabButton, { label: "Hot", active: tab === "hot", onClick: () => setTab("hot") })
|
|
3736
|
+
] }),
|
|
3737
|
+
/* @__PURE__ */ jsx(Input, { placeholder: "Search skills...", value: search, onChange: (e) => setSearch(e.target.value) }),
|
|
3738
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
3739
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex gap-4 min-h-0", children: [
|
|
3740
|
+
/* @__PURE__ */ jsx("div", { className: "w-1/2 overflow-y-auto border border-muted-foreground/25 rounded-lg", children: loading ? /* @__PURE__ */ jsx("div", { className: "p-4 text-sm text-muted-foreground", children: "Loading skills..." }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-sm text-muted-foreground", children: search ? "No skills match your search" : "No skills found" }) : filtered.map((entry) => /* @__PURE__ */ jsxs(
|
|
3741
|
+
"button",
|
|
3742
|
+
{
|
|
3743
|
+
onClick: () => handleSelect(entry),
|
|
3744
|
+
className: `w-full text-left px-3 py-2 border-b border-muted-foreground/10 hover:bg-muted/50 transition-colors ${selected?.skill === entry.skill && selected?.owner === entry.owner ? "bg-muted/50" : ""}`,
|
|
3745
|
+
children: [
|
|
3746
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium text-sm truncate", children: entry.name }),
|
|
3747
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3748
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
|
|
3749
|
+
entry.owner,
|
|
3750
|
+
"/",
|
|
3751
|
+
entry.repo
|
|
3752
|
+
] }),
|
|
3753
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground font-mono ml-2 shrink-0", children: entry.installs })
|
|
3754
|
+
] })
|
|
3755
|
+
]
|
|
3756
|
+
},
|
|
3757
|
+
`${entry.owner}/${entry.repo}/${entry.skill}`
|
|
3758
|
+
)) }),
|
|
3759
|
+
/* @__PURE__ */ jsx("div", { className: "w-1/2 overflow-y-auto border border-muted-foreground/25 rounded-lg p-3", children: !selected ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Select a skill to preview" }) : previewLoading ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Loading preview..." }) : /* @__PURE__ */ jsx("pre", { className: "text-xs whitespace-pre-wrap break-words font-mono", children: preview }) })
|
|
3760
|
+
] }),
|
|
3761
|
+
importError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: importError }),
|
|
3762
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-muted-foreground/25 pt-3", children: /* @__PURE__ */ jsx(FormField, { label: "Or paste a skills.sh URL", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
3763
|
+
/* @__PURE__ */ jsx(
|
|
3764
|
+
Input,
|
|
3765
|
+
{
|
|
3766
|
+
value: url,
|
|
3767
|
+
onChange: (e) => setUrl(e.target.value),
|
|
3768
|
+
placeholder: "skills.sh/owner/repo/skill",
|
|
3769
|
+
onKeyDown: (e) => e.key === "Enter" && handleUrlImport(),
|
|
3770
|
+
className: "flex-1"
|
|
3771
|
+
}
|
|
3772
|
+
),
|
|
3773
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: handleUrlImport, disabled: urlImporting || !url.trim(), children: urlImporting ? "Importing..." : "Import URL" })
|
|
3774
|
+
] }) }) })
|
|
3775
|
+
] }),
|
|
3776
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3777
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3778
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleImport, disabled: !selected || importing || previewLoading, children: importing ? "Importing..." : "Import Selected" })
|
|
3779
|
+
] })
|
|
3780
|
+
] }) });
|
|
3781
|
+
}
|
|
3609
3782
|
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3610
3783
|
const client = useAgentPlaneClient();
|
|
3784
|
+
const [importOpen, setImportOpen] = useState(false);
|
|
3785
|
+
const [extraSkills, setExtraSkills] = useState([]);
|
|
3786
|
+
const allSkills = useMemo(() => [...initialSkills, ...extraSkills], [initialSkills, extraSkills]);
|
|
3611
3787
|
const initialFiles = useMemo(
|
|
3612
|
-
() =>
|
|
3788
|
+
() => allSkills.flatMap(
|
|
3613
3789
|
(s) => s.files.map((f) => ({
|
|
3614
3790
|
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3615
3791
|
content: f.content
|
|
3616
3792
|
}))
|
|
3617
3793
|
),
|
|
3618
|
-
[
|
|
3794
|
+
[allSkills]
|
|
3619
3795
|
);
|
|
3796
|
+
const existingFolders = useMemo(() => allSkills.map((s) => s.folder), [allSkills]);
|
|
3797
|
+
const handleImported = useCallback((skill) => {
|
|
3798
|
+
setExtraSkills((prev) => [...prev, skill]);
|
|
3799
|
+
}, []);
|
|
3620
3800
|
const handleSave = useCallback(async (files) => {
|
|
3621
3801
|
const folderMap = /* @__PURE__ */ new Map();
|
|
3622
3802
|
for (const file of files) {
|
|
@@ -3635,22 +3815,35 @@ function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
|
3635
3815
|
}
|
|
3636
3816
|
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3637
3817
|
await client.agents.update(agentId, { skills });
|
|
3818
|
+
setExtraSkills([]);
|
|
3638
3819
|
onSaved?.();
|
|
3639
3820
|
}, [agentId, client, onSaved]);
|
|
3640
|
-
return /* @__PURE__ */
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3821
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3822
|
+
/* @__PURE__ */ jsx(
|
|
3823
|
+
FileTreeEditor2,
|
|
3824
|
+
{
|
|
3825
|
+
initialFiles,
|
|
3826
|
+
onSave: handleSave,
|
|
3827
|
+
title: "Skills",
|
|
3828
|
+
saveLabel: "Save Skills",
|
|
3829
|
+
addFolderLabel: "Skill",
|
|
3830
|
+
newFileTemplate: {
|
|
3831
|
+
filename: "SKILL.md",
|
|
3832
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3833
|
+
},
|
|
3834
|
+
headerActions: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => setImportOpen(true), children: "Import from skills.sh" })
|
|
3651
3835
|
}
|
|
3652
|
-
|
|
3653
|
-
|
|
3836
|
+
),
|
|
3837
|
+
/* @__PURE__ */ jsx(
|
|
3838
|
+
ImportSkillDialog,
|
|
3839
|
+
{
|
|
3840
|
+
open: importOpen,
|
|
3841
|
+
onOpenChange: setImportOpen,
|
|
3842
|
+
onImported: handleImported,
|
|
3843
|
+
existingFolders
|
|
3844
|
+
}
|
|
3845
|
+
)
|
|
3846
|
+
] });
|
|
3654
3847
|
}
|
|
3655
3848
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3656
3849
|
const client = useAgentPlaneClient();
|