@getcatalystiq/agent-plane-ui 0.1.27 → 0.1.29

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.
@@ -0,0 +1,2 @@
1
+ export { AgentIdentityTab } from './chunk-WUTZ6WUU.js';
2
+ import './chunk-XFI227OB.js';
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunk7EBIKKRM_cjs = require('./chunk-7EBIKKRM.cjs');
3
+ var chunkHGSMFQH7_cjs = require('./chunk-HGSMFQH7.cjs');
4
4
  require('./chunk-XXF4U7WL.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "AgentIdentityTab", {
9
9
  enumerable: true,
10
- get: function () { return chunk7EBIKKRM_cjs.AgentIdentityTab; }
10
+ get: function () { return chunkHGSMFQH7_cjs.AgentIdentityTab; }
11
11
  });
@@ -214,11 +214,7 @@ function AgentIdentityTab({
214
214
  onSave: handleSave,
215
215
  title: "SoulSpec",
216
216
  saveLabel: saving ? "Saving..." : "Save Identity",
217
- addFolderLabel: "Folder",
218
- newFileTemplate: {
219
- filename: "CUSTOM.md",
220
- content: "# Custom\n\nAdd custom identity content...\n"
221
- },
217
+ fixedStructure: true,
222
218
  savedVersion
223
219
  }
224
220
  ) }),
@@ -212,11 +212,7 @@ function AgentIdentityTab({
212
212
  onSave: handleSave,
213
213
  title: "SoulSpec",
214
214
  saveLabel: saving ? "Saving..." : "Save Identity",
215
- addFolderLabel: "Folder",
216
- newFileTemplate: {
217
- filename: "CUSTOM.md",
218
- content: "# Custom\n\nAdd custom identity content...\n"
219
- },
215
+ fixedStructure: true,
220
216
  savedVersion
221
217
  }
222
218
  ) }),
package/dist/editor.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkVZ43ATC5_cjs = require('./chunk-VZ43ATC5.cjs');
4
- var chunk7EBIKKRM_cjs = require('./chunk-7EBIKKRM.cjs');
4
+ var chunkHGSMFQH7_cjs = require('./chunk-HGSMFQH7.cjs');
5
5
  var chunkXXF4U7WL_cjs = require('./chunk-XXF4U7WL.cjs');
6
6
  var react = require('react');
7
7
  var CodeMirror = require('@uiw/react-codemirror');
@@ -187,6 +187,6 @@ Object.defineProperty(exports, "FileTreeEditor", {
187
187
  });
188
188
  Object.defineProperty(exports, "AgentIdentityTab", {
189
189
  enumerable: true,
190
- get: function () { return chunk7EBIKKRM_cjs.AgentIdentityTab; }
190
+ get: function () { return chunkHGSMFQH7_cjs.AgentIdentityTab; }
191
191
  });
192
192
  exports.PluginEditorPage = PluginEditorPage;
package/dist/editor.d.cts CHANGED
@@ -50,12 +50,8 @@ interface AgentIdentityTabProps {
50
50
  onSave: (files: FlatFile[]) => Promise<void>;
51
51
  title?: string;
52
52
  saveLabel?: string;
53
- addFolderLabel?: string;
54
- newFileTemplate?: {
55
- filename: string;
56
- content: string;
57
- };
58
53
  savedVersion?: number;
54
+ fixedStructure?: boolean;
59
55
  }>;
60
56
  /** Called after a successful save so the host can refresh data. */
61
57
  onSaved?: () => void;
package/dist/editor.d.ts CHANGED
@@ -50,12 +50,8 @@ interface AgentIdentityTabProps {
50
50
  onSave: (files: FlatFile[]) => Promise<void>;
51
51
  title?: string;
52
52
  saveLabel?: string;
53
- addFolderLabel?: string;
54
- newFileTemplate?: {
55
- filename: string;
56
- content: string;
57
- };
58
53
  savedVersion?: number;
54
+ fixedStructure?: boolean;
59
55
  }>;
60
56
  /** Called after a successful save so the host can refresh data. */
61
57
  onSaved?: () => void;
package/dist/editor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useApi, Skeleton, FileTreeEditor, Card, CardHeader, CardTitle, CardContent } from './chunk-CE2RHDPY.js';
2
2
  export { FileTreeEditor } from './chunk-CE2RHDPY.js';
3
- export { AgentIdentityTab } from './chunk-UMNJ5SV6.js';
3
+ export { AgentIdentityTab } from './chunk-WUTZ6WUU.js';
4
4
  import { useNavigation, useAgentPlaneClient, Badge, Button } from './chunk-XFI227OB.js';
5
5
  import { useState, useCallback } from 'react';
6
6
  import CodeMirror from '@uiw/react-codemirror';
package/dist/index.cjs CHANGED
@@ -3361,7 +3361,8 @@ function FileTreeEditor2({
3361
3361
  saveLabel = "Save",
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
- savedVersion
3364
+ savedVersion,
3365
+ fixedStructure = false
3365
3366
  }) {
3366
3367
  const [files, setFiles] = React.useState(initialFiles);
3367
3368
  const [selectedPath, setSelectedPath] = React.useState(
@@ -3476,7 +3477,7 @@ function FileTreeEditor2({
3476
3477
  node.name,
3477
3478
  "/"
3478
3479
  ] }),
3479
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
3480
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsxRuntime.jsx(
3480
3481
  "button",
3481
3482
  {
3482
3483
  onClick: (e) => {
@@ -3502,7 +3503,7 @@ function FileTreeEditor2({
3502
3503
  onClick: () => setSelectedPath(file.path),
3503
3504
  children: [
3504
3505
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: fileName }),
3505
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
3506
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsxRuntime.jsx(
3506
3507
  "button",
3507
3508
  {
3508
3509
  onClick: (e) => {
@@ -3518,7 +3519,7 @@ function FileTreeEditor2({
3518
3519
  file.path
3519
3520
  );
3520
3521
  }),
3521
- !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: [
3522
+ !readOnly && !fixedStructure && /* @__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: [
3522
3523
  /* @__PURE__ */ jsxRuntime.jsx(
3523
3524
  chunkXXF4U7WL_cjs.Input,
3524
3525
  {
@@ -3558,7 +3559,7 @@ function FileTreeEditor2({
3558
3559
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
3559
3560
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
3560
3561
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
3561
- !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
3562
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsxRuntime.jsxs(
3562
3563
  "button",
3563
3564
  {
3564
3565
  onClick: () => setShowAddFolder(!showAddFolder),
@@ -3570,7 +3571,7 @@ function FileTreeEditor2({
3570
3571
  }
3571
3572
  )
3572
3573
  ] }),
3573
- showAddFolder && !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
3574
+ showAddFolder && !readOnly && !fixedStructure && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
3574
3575
  /* @__PURE__ */ jsxRuntime.jsx(
3575
3576
  chunkXXF4U7WL_cjs.Input,
3576
3577
  {
@@ -3593,7 +3594,7 @@ function FileTreeEditor2({
3593
3594
  onClick: () => setSelectedPath(file.path),
3594
3595
  children: [
3595
3596
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: file.path }),
3596
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
3597
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsxRuntime.jsx(
3597
3598
  "button",
3598
3599
  {
3599
3600
  onClick: (e) => {
@@ -3629,17 +3630,193 @@ function FileTreeEditor2({
3629
3630
  ] })
3630
3631
  ] });
3631
3632
  }
3633
+ function TabButton({ label, active, onClick }) {
3634
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3635
+ "button",
3636
+ {
3637
+ onClick,
3638
+ className: `relative pb-2 text-sm font-medium transition-colors ${active ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
3639
+ children: [
3640
+ label,
3641
+ active && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground rounded-full" })
3642
+ ]
3643
+ }
3644
+ );
3645
+ }
3646
+ function ImportSkillDialog({ open, onOpenChange, onImported, existingFolders }) {
3647
+ const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
3648
+ const [tab, setTab] = React.useState("all");
3649
+ const [entries, setEntries] = React.useState([]);
3650
+ const [loading, setLoading] = React.useState(false);
3651
+ const [error, setError] = React.useState("");
3652
+ const [search, setSearch] = React.useState("");
3653
+ const [selected, setSelected] = React.useState(null);
3654
+ const [preview, setPreview] = React.useState("");
3655
+ const [previewLoading, setPreviewLoading] = React.useState(false);
3656
+ const [importing, setImporting] = React.useState(false);
3657
+ const [importError, setImportError] = React.useState("");
3658
+ const [url, setUrl] = React.useState("");
3659
+ const [urlImporting, setUrlImporting] = React.useState(false);
3660
+ React.useEffect(() => {
3661
+ if (!open) return;
3662
+ setLoading(true);
3663
+ setError("");
3664
+ setSelected(null);
3665
+ setPreview("");
3666
+ client.skillsDirectory.list(tab).then((data) => setEntries(data)).catch((err) => setError(err instanceof Error ? err.message : "Failed to load skills")).finally(() => setLoading(false));
3667
+ }, [tab, open, client]);
3668
+ const filtered = React.useMemo(() => {
3669
+ if (!search.trim()) return entries;
3670
+ const q = search.toLowerCase();
3671
+ return entries.filter(
3672
+ (e) => e.name.toLowerCase().includes(q) || e.owner.toLowerCase().includes(q) || e.repo.toLowerCase().includes(q)
3673
+ );
3674
+ }, [entries, search]);
3675
+ async function handleSelect(entry) {
3676
+ setSelected(entry);
3677
+ setPreviewLoading(true);
3678
+ setPreview("");
3679
+ setImportError("");
3680
+ try {
3681
+ const content = await client.skillsDirectory.preview(entry.owner, entry.repo, entry.skill);
3682
+ setPreview(content);
3683
+ } catch (err) {
3684
+ setPreview(`Failed to load preview: ${err instanceof Error ? err.message : "Unknown error"}`);
3685
+ } finally {
3686
+ setPreviewLoading(false);
3687
+ }
3688
+ }
3689
+ async function handleImport() {
3690
+ if (!selected) return;
3691
+ setImporting(true);
3692
+ setImportError("");
3693
+ try {
3694
+ const result = await client.skillsDirectory.import({
3695
+ owner: selected.owner,
3696
+ repo: selected.repo,
3697
+ skill_name: selected.skill
3698
+ });
3699
+ if (existingFolders.includes(result.folder)) {
3700
+ setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
3701
+ return;
3702
+ }
3703
+ if (result.warnings.length > 0) {
3704
+ setImportError(`Imported with warnings: ${result.warnings.join("; ")}`);
3705
+ }
3706
+ onImported({ folder: result.folder, files: result.files });
3707
+ onOpenChange(false);
3708
+ resetState();
3709
+ } catch (err) {
3710
+ setImportError(err instanceof Error ? err.message : "Failed to import skill");
3711
+ } finally {
3712
+ setImporting(false);
3713
+ }
3714
+ }
3715
+ async function handleUrlImport() {
3716
+ if (!url.trim()) return;
3717
+ setUrlImporting(true);
3718
+ setImportError("");
3719
+ try {
3720
+ const result = await client.skillsDirectory.import({ url: url.trim() });
3721
+ if (existingFolders.includes(result.folder)) {
3722
+ setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
3723
+ return;
3724
+ }
3725
+ onImported({ folder: result.folder, files: result.files });
3726
+ onOpenChange(false);
3727
+ resetState();
3728
+ } catch (err) {
3729
+ setImportError(err instanceof Error ? err.message : "Failed to import skill");
3730
+ } finally {
3731
+ setUrlImporting(false);
3732
+ }
3733
+ }
3734
+ function resetState() {
3735
+ setTab("all");
3736
+ setSearch("");
3737
+ setSelected(null);
3738
+ setPreview("");
3739
+ setUrl("");
3740
+ setError("");
3741
+ setImportError("");
3742
+ }
3743
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Dialog, { open, onOpenChange: (v) => {
3744
+ onOpenChange(v);
3745
+ if (!v) resetState();
3746
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
3747
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogHeader, { children: [
3748
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.DialogTitle, { children: "Import from skills.sh" }),
3749
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.DialogDescription, { children: "Browse the open skills directory and import skills into this agent." })
3750
+ ] }),
3751
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogBody, { className: "flex-1 overflow-hidden flex flex-col gap-4", children: [
3752
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
3753
+ /* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "All Time", active: tab === "all", onClick: () => setTab("all") }),
3754
+ /* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Trending", active: tab === "trending", onClick: () => setTab("trending") }),
3755
+ /* @__PURE__ */ jsxRuntime.jsx(TabButton, { label: "Hot", active: tab === "hot", onClick: () => setTab("hot") })
3756
+ ] }),
3757
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Input, { placeholder: "Search skills...", value: search, onChange: (e) => setSearch(e.target.value) }),
3758
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
3759
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden flex gap-4 min-h-0", children: [
3760
+ /* @__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(
3761
+ "button",
3762
+ {
3763
+ onClick: () => handleSelect(entry),
3764
+ 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" : ""}`,
3765
+ children: [
3766
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-sm truncate", children: entry.name }),
3767
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
3768
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
3769
+ entry.owner,
3770
+ "/",
3771
+ entry.repo
3772
+ ] }),
3773
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-mono ml-2 shrink-0", children: entry.installs })
3774
+ ] })
3775
+ ]
3776
+ },
3777
+ `${entry.owner}/${entry.repo}/${entry.skill}`
3778
+ )) }),
3779
+ /* @__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 }) })
3780
+ ] }),
3781
+ importError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: importError }),
3782
+ /* @__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: [
3783
+ /* @__PURE__ */ jsxRuntime.jsx(
3784
+ chunkXXF4U7WL_cjs.Input,
3785
+ {
3786
+ value: url,
3787
+ onChange: (e) => setUrl(e.target.value),
3788
+ placeholder: "skills.sh/owner/repo/skill",
3789
+ onKeyDown: (e) => e.key === "Enter" && handleUrlImport(),
3790
+ className: "flex-1"
3791
+ }
3792
+ ),
3793
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", variant: "outline", onClick: handleUrlImport, disabled: urlImporting || !url.trim(), children: urlImporting ? "Importing..." : "Import URL" })
3794
+ ] }) }) })
3795
+ ] }),
3796
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkXXF4U7WL_cjs.DialogFooter, { children: [
3797
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }),
3798
+ /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", onClick: handleImport, disabled: !selected || importing || previewLoading, children: importing ? "Importing..." : "Import Selected" })
3799
+ ] })
3800
+ ] }) });
3801
+ }
3632
3802
  function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3633
3803
  const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
3804
+ const [importOpen, setImportOpen] = React.useState(false);
3805
+ const [extraSkills, setExtraSkills] = React.useState([]);
3806
+ const allSkills = React.useMemo(() => [...initialSkills, ...extraSkills], [initialSkills, extraSkills]);
3634
3807
  const initialFiles = React.useMemo(
3635
- () => initialSkills.flatMap(
3808
+ () => allSkills.flatMap(
3636
3809
  (s) => s.files.map((f) => ({
3637
3810
  path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
3638
3811
  content: f.content
3639
3812
  }))
3640
3813
  ),
3641
- [initialSkills]
3814
+ [allSkills]
3642
3815
  );
3816
+ const existingFolders = React.useMemo(() => allSkills.map((s) => s.folder), [allSkills]);
3817
+ const handleImported = React.useCallback((skill) => {
3818
+ setExtraSkills((prev) => [...prev, skill]);
3819
+ }, []);
3643
3820
  const handleSave = React.useCallback(async (files) => {
3644
3821
  const folderMap = /* @__PURE__ */ new Map();
3645
3822
  for (const file of files) {
@@ -3658,22 +3835,35 @@ function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3658
3835
  }
3659
3836
  const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
3660
3837
  await client.agents.update(agentId, { skills });
3838
+ setExtraSkills([]);
3661
3839
  onSaved?.();
3662
3840
  }, [agentId, client, onSaved]);
3663
- return /* @__PURE__ */ jsxRuntime.jsx(
3664
- FileTreeEditor2,
3665
- {
3666
- initialFiles,
3667
- onSave: handleSave,
3668
- title: "Skills",
3669
- saveLabel: "Save Skills",
3670
- addFolderLabel: "Skill",
3671
- newFileTemplate: {
3672
- filename: "SKILL.md",
3673
- content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
3841
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3842
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(chunkXXF4U7WL_cjs.Button, { size: "sm", variant: "outline", onClick: () => setImportOpen(true), children: "Import from skills.sh" }) }),
3843
+ /* @__PURE__ */ jsxRuntime.jsx(
3844
+ FileTreeEditor2,
3845
+ {
3846
+ initialFiles,
3847
+ onSave: handleSave,
3848
+ title: "Skills",
3849
+ saveLabel: "Save Skills",
3850
+ addFolderLabel: "Skill",
3851
+ newFileTemplate: {
3852
+ filename: "SKILL.md",
3853
+ content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
3854
+ }
3674
3855
  }
3675
- }
3676
- );
3856
+ ),
3857
+ /* @__PURE__ */ jsxRuntime.jsx(
3858
+ ImportSkillDialog,
3859
+ {
3860
+ open: importOpen,
3861
+ onOpenChange: setImportOpen,
3862
+ onImported: handleImported,
3863
+ existingFolders
3864
+ }
3865
+ )
3866
+ ] });
3677
3867
  }
3678
3868
  function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
3679
3869
  const client = chunkXXF4U7WL_cjs.useAgentPlaneClient();
@@ -4069,7 +4259,7 @@ function AgentA2aInfo({
4069
4259
  ] })
4070
4260
  ] });
4071
4261
  }
4072
- var AgentIdentityTab = React.lazy(() => import('./agent-identity-tab-HADOCEAN.cjs').then((m) => ({ default: m.AgentIdentityTab })));
4262
+ var AgentIdentityTab = React.lazy(() => import('./agent-identity-tab-YGMVWOWT.cjs').then((m) => ({ default: m.AgentIdentityTab })));
4073
4263
  function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, adminApiBaseUrl, adminApiKey }) {
4074
4264
  const { LinkComponent, basePath } = chunkXXF4U7WL_cjs.useNavigation();
4075
4265
  const { mutate } = swr.useSWRConfig();
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
@@ -3337,7 +3337,8 @@ function FileTreeEditor2({
3337
3337
  saveLabel = "Save",
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
- savedVersion
3340
+ savedVersion,
3341
+ fixedStructure = false
3341
3342
  }) {
3342
3343
  const [files, setFiles] = useState(initialFiles);
3343
3344
  const [selectedPath, setSelectedPath] = useState(
@@ -3452,7 +3453,7 @@ function FileTreeEditor2({
3452
3453
  node.name,
3453
3454
  "/"
3454
3455
  ] }),
3455
- !readOnly && /* @__PURE__ */ jsx(
3456
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsx(
3456
3457
  "button",
3457
3458
  {
3458
3459
  onClick: (e) => {
@@ -3478,7 +3479,7 @@ function FileTreeEditor2({
3478
3479
  onClick: () => setSelectedPath(file.path),
3479
3480
  children: [
3480
3481
  /* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: fileName }),
3481
- !readOnly && /* @__PURE__ */ jsx(
3482
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsx(
3482
3483
  "button",
3483
3484
  {
3484
3485
  onClick: (e) => {
@@ -3494,7 +3495,7 @@ function FileTreeEditor2({
3494
3495
  file.path
3495
3496
  );
3496
3497
  }),
3497
- !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: [
3498
+ !readOnly && !fixedStructure && /* @__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: [
3498
3499
  /* @__PURE__ */ jsx(
3499
3500
  Input,
3500
3501
  {
@@ -3534,7 +3535,7 @@ function FileTreeEditor2({
3534
3535
  /* @__PURE__ */ jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
3535
3536
  /* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
3536
3537
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
3537
- !readOnly && /* @__PURE__ */ jsxs(
3538
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsxs(
3538
3539
  "button",
3539
3540
  {
3540
3541
  onClick: () => setShowAddFolder(!showAddFolder),
@@ -3546,7 +3547,7 @@ function FileTreeEditor2({
3546
3547
  }
3547
3548
  )
3548
3549
  ] }),
3549
- showAddFolder && !readOnly && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
3550
+ showAddFolder && !readOnly && !fixedStructure && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
3550
3551
  /* @__PURE__ */ jsx(
3551
3552
  Input,
3552
3553
  {
@@ -3569,7 +3570,7 @@ function FileTreeEditor2({
3569
3570
  onClick: () => setSelectedPath(file.path),
3570
3571
  children: [
3571
3572
  /* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: file.path }),
3572
- !readOnly && /* @__PURE__ */ jsx(
3573
+ !readOnly && !fixedStructure && /* @__PURE__ */ jsx(
3573
3574
  "button",
3574
3575
  {
3575
3576
  onClick: (e) => {
@@ -3605,17 +3606,193 @@ function FileTreeEditor2({
3605
3606
  ] })
3606
3607
  ] });
3607
3608
  }
3609
+ function TabButton({ label, active, onClick }) {
3610
+ return /* @__PURE__ */ jsxs(
3611
+ "button",
3612
+ {
3613
+ onClick,
3614
+ className: `relative pb-2 text-sm font-medium transition-colors ${active ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
3615
+ children: [
3616
+ label,
3617
+ active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground rounded-full" })
3618
+ ]
3619
+ }
3620
+ );
3621
+ }
3622
+ function ImportSkillDialog({ open, onOpenChange, onImported, existingFolders }) {
3623
+ const client = useAgentPlaneClient();
3624
+ const [tab, setTab] = useState("all");
3625
+ const [entries, setEntries] = useState([]);
3626
+ const [loading, setLoading] = useState(false);
3627
+ const [error, setError] = useState("");
3628
+ const [search, setSearch] = useState("");
3629
+ const [selected, setSelected] = useState(null);
3630
+ const [preview, setPreview] = useState("");
3631
+ const [previewLoading, setPreviewLoading] = useState(false);
3632
+ const [importing, setImporting] = useState(false);
3633
+ const [importError, setImportError] = useState("");
3634
+ const [url, setUrl] = useState("");
3635
+ const [urlImporting, setUrlImporting] = useState(false);
3636
+ useEffect(() => {
3637
+ if (!open) return;
3638
+ setLoading(true);
3639
+ setError("");
3640
+ setSelected(null);
3641
+ setPreview("");
3642
+ client.skillsDirectory.list(tab).then((data) => setEntries(data)).catch((err) => setError(err instanceof Error ? err.message : "Failed to load skills")).finally(() => setLoading(false));
3643
+ }, [tab, open, client]);
3644
+ const filtered = useMemo(() => {
3645
+ if (!search.trim()) return entries;
3646
+ const q = search.toLowerCase();
3647
+ return entries.filter(
3648
+ (e) => e.name.toLowerCase().includes(q) || e.owner.toLowerCase().includes(q) || e.repo.toLowerCase().includes(q)
3649
+ );
3650
+ }, [entries, search]);
3651
+ async function handleSelect(entry) {
3652
+ setSelected(entry);
3653
+ setPreviewLoading(true);
3654
+ setPreview("");
3655
+ setImportError("");
3656
+ try {
3657
+ const content = await client.skillsDirectory.preview(entry.owner, entry.repo, entry.skill);
3658
+ setPreview(content);
3659
+ } catch (err) {
3660
+ setPreview(`Failed to load preview: ${err instanceof Error ? err.message : "Unknown error"}`);
3661
+ } finally {
3662
+ setPreviewLoading(false);
3663
+ }
3664
+ }
3665
+ async function handleImport() {
3666
+ if (!selected) return;
3667
+ setImporting(true);
3668
+ setImportError("");
3669
+ try {
3670
+ const result = await client.skillsDirectory.import({
3671
+ owner: selected.owner,
3672
+ repo: selected.repo,
3673
+ skill_name: selected.skill
3674
+ });
3675
+ if (existingFolders.includes(result.folder)) {
3676
+ setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
3677
+ return;
3678
+ }
3679
+ if (result.warnings.length > 0) {
3680
+ setImportError(`Imported with warnings: ${result.warnings.join("; ")}`);
3681
+ }
3682
+ onImported({ folder: result.folder, files: result.files });
3683
+ onOpenChange(false);
3684
+ resetState();
3685
+ } catch (err) {
3686
+ setImportError(err instanceof Error ? err.message : "Failed to import skill");
3687
+ } finally {
3688
+ setImporting(false);
3689
+ }
3690
+ }
3691
+ async function handleUrlImport() {
3692
+ if (!url.trim()) return;
3693
+ setUrlImporting(true);
3694
+ setImportError("");
3695
+ try {
3696
+ const result = await client.skillsDirectory.import({ url: url.trim() });
3697
+ if (existingFolders.includes(result.folder)) {
3698
+ setImportError(`Skill folder "${result.folder}" already exists. Remove it first or rename.`);
3699
+ return;
3700
+ }
3701
+ onImported({ folder: result.folder, files: result.files });
3702
+ onOpenChange(false);
3703
+ resetState();
3704
+ } catch (err) {
3705
+ setImportError(err instanceof Error ? err.message : "Failed to import skill");
3706
+ } finally {
3707
+ setUrlImporting(false);
3708
+ }
3709
+ }
3710
+ function resetState() {
3711
+ setTab("all");
3712
+ setSearch("");
3713
+ setSelected(null);
3714
+ setPreview("");
3715
+ setUrl("");
3716
+ setError("");
3717
+ setImportError("");
3718
+ }
3719
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (v) => {
3720
+ onOpenChange(v);
3721
+ if (!v) resetState();
3722
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
3723
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
3724
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Import from skills.sh" }),
3725
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Browse the open skills directory and import skills into this agent." })
3726
+ ] }),
3727
+ /* @__PURE__ */ jsxs(DialogBody, { className: "flex-1 overflow-hidden flex flex-col gap-4", children: [
3728
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
3729
+ /* @__PURE__ */ jsx(TabButton, { label: "All Time", active: tab === "all", onClick: () => setTab("all") }),
3730
+ /* @__PURE__ */ jsx(TabButton, { label: "Trending", active: tab === "trending", onClick: () => setTab("trending") }),
3731
+ /* @__PURE__ */ jsx(TabButton, { label: "Hot", active: tab === "hot", onClick: () => setTab("hot") })
3732
+ ] }),
3733
+ /* @__PURE__ */ jsx(Input, { placeholder: "Search skills...", value: search, onChange: (e) => setSearch(e.target.value) }),
3734
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
3735
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex gap-4 min-h-0", children: [
3736
+ /* @__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(
3737
+ "button",
3738
+ {
3739
+ onClick: () => handleSelect(entry),
3740
+ 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" : ""}`,
3741
+ children: [
3742
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-sm truncate", children: entry.name }),
3743
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3744
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
3745
+ entry.owner,
3746
+ "/",
3747
+ entry.repo
3748
+ ] }),
3749
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground font-mono ml-2 shrink-0", children: entry.installs })
3750
+ ] })
3751
+ ]
3752
+ },
3753
+ `${entry.owner}/${entry.repo}/${entry.skill}`
3754
+ )) }),
3755
+ /* @__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 }) })
3756
+ ] }),
3757
+ importError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: importError }),
3758
+ /* @__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: [
3759
+ /* @__PURE__ */ jsx(
3760
+ Input,
3761
+ {
3762
+ value: url,
3763
+ onChange: (e) => setUrl(e.target.value),
3764
+ placeholder: "skills.sh/owner/repo/skill",
3765
+ onKeyDown: (e) => e.key === "Enter" && handleUrlImport(),
3766
+ className: "flex-1"
3767
+ }
3768
+ ),
3769
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: handleUrlImport, disabled: urlImporting || !url.trim(), children: urlImporting ? "Importing..." : "Import URL" })
3770
+ ] }) }) })
3771
+ ] }),
3772
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
3773
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }),
3774
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleImport, disabled: !selected || importing || previewLoading, children: importing ? "Importing..." : "Import Selected" })
3775
+ ] })
3776
+ ] }) });
3777
+ }
3608
3778
  function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3609
3779
  const client = useAgentPlaneClient();
3780
+ const [importOpen, setImportOpen] = useState(false);
3781
+ const [extraSkills, setExtraSkills] = useState([]);
3782
+ const allSkills = useMemo(() => [...initialSkills, ...extraSkills], [initialSkills, extraSkills]);
3610
3783
  const initialFiles = useMemo(
3611
- () => initialSkills.flatMap(
3784
+ () => allSkills.flatMap(
3612
3785
  (s) => s.files.map((f) => ({
3613
3786
  path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
3614
3787
  content: f.content
3615
3788
  }))
3616
3789
  ),
3617
- [initialSkills]
3790
+ [allSkills]
3618
3791
  );
3792
+ const existingFolders = useMemo(() => allSkills.map((s) => s.folder), [allSkills]);
3793
+ const handleImported = useCallback((skill) => {
3794
+ setExtraSkills((prev) => [...prev, skill]);
3795
+ }, []);
3619
3796
  const handleSave = useCallback(async (files) => {
3620
3797
  const folderMap = /* @__PURE__ */ new Map();
3621
3798
  for (const file of files) {
@@ -3634,22 +3811,35 @@ function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3634
3811
  }
3635
3812
  const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
3636
3813
  await client.agents.update(agentId, { skills });
3814
+ setExtraSkills([]);
3637
3815
  onSaved?.();
3638
3816
  }, [agentId, client, onSaved]);
3639
- return /* @__PURE__ */ jsx(
3640
- FileTreeEditor2,
3641
- {
3642
- initialFiles,
3643
- onSave: handleSave,
3644
- title: "Skills",
3645
- saveLabel: "Save Skills",
3646
- addFolderLabel: "Skill",
3647
- newFileTemplate: {
3648
- filename: "SKILL.md",
3649
- content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
3817
+ return /* @__PURE__ */ jsxs("div", { children: [
3818
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end mb-4", children: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => setImportOpen(true), children: "Import from skills.sh" }) }),
3819
+ /* @__PURE__ */ jsx(
3820
+ FileTreeEditor2,
3821
+ {
3822
+ initialFiles,
3823
+ onSave: handleSave,
3824
+ title: "Skills",
3825
+ saveLabel: "Save Skills",
3826
+ addFolderLabel: "Skill",
3827
+ newFileTemplate: {
3828
+ filename: "SKILL.md",
3829
+ content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
3830
+ }
3650
3831
  }
3651
- }
3652
- );
3832
+ ),
3833
+ /* @__PURE__ */ jsx(
3834
+ ImportSkillDialog,
3835
+ {
3836
+ open: importOpen,
3837
+ onOpenChange: setImportOpen,
3838
+ onImported: handleImported,
3839
+ existingFolders
3840
+ }
3841
+ )
3842
+ ] });
3653
3843
  }
3654
3844
  function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
3655
3845
  const client = useAgentPlaneClient();
@@ -4045,7 +4235,7 @@ function AgentA2aInfo({
4045
4235
  ] })
4046
4236
  ] });
4047
4237
  }
4048
- var AgentIdentityTab = lazy(() => import('./agent-identity-tab-LROKQ7HM.js').then((m) => ({ default: m.AgentIdentityTab })));
4238
+ var AgentIdentityTab = lazy(() => import('./agent-identity-tab-BP5MPDYZ.js').then((m) => ({ default: m.AgentIdentityTab })));
4049
4239
  function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, adminApiBaseUrl, adminApiKey }) {
4050
4240
  const { LinkComponent, basePath } = useNavigation();
4051
4241
  const { mutate } = useSWRConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getcatalystiq/agent-plane-ui",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "Embeddable React component library for AgentPlane",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,2 +0,0 @@
1
- export { AgentIdentityTab } from './chunk-UMNJ5SV6.js';
2
- import './chunk-XFI227OB.js';