@getcatalystiq/agent-plane-ui 0.1.18 → 0.1.19

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/editor.d.ts CHANGED
@@ -28,4 +28,53 @@ interface PluginEditorPageProps {
28
28
  }
29
29
  declare function PluginEditorPage({ marketplaceId, pluginName }: PluginEditorPageProps): react_jsx_runtime.JSX.Element;
30
30
 
31
- export { FileTreeEditor, type FileTreeEditorProps, type FlatFile, PluginEditorPage, type PluginEditorPageProps };
31
+ interface Agent {
32
+ id: string;
33
+ soul_md: string | null;
34
+ identity_md: string | null;
35
+ style_md: string | null;
36
+ agents_md: string | null;
37
+ heartbeat_md: string | null;
38
+ user_template_md: string | null;
39
+ examples_good_md: string | null;
40
+ examples_bad_md: string | null;
41
+ }
42
+ interface AgentIdentityTabProps {
43
+ agent: Agent;
44
+ /**
45
+ * The FileTreeEditor component from the /editor entry point.
46
+ * Pass it as a prop to keep CodeMirror out of the core bundle.
47
+ */
48
+ FileTreeEditor: React.ComponentType<{
49
+ initialFiles: FlatFile[];
50
+ onSave: (files: FlatFile[]) => Promise<void>;
51
+ title?: string;
52
+ saveLabel?: string;
53
+ addFolderLabel?: string;
54
+ newFileTemplate?: {
55
+ filename: string;
56
+ content: string;
57
+ };
58
+ savedVersion?: number;
59
+ }>;
60
+ /** Called after a successful save so the host can refresh data. */
61
+ onSaved?: () => void;
62
+ /** Generate a SoulSpec for this agent. Returns a map of field-name/path to content. */
63
+ onGenerateSoul?: () => Promise<{
64
+ files: Record<string, string>;
65
+ }>;
66
+ /** Import a SoulSpec from the ClawSouls registry. */
67
+ onImportSoul?: (ref: string) => Promise<{
68
+ files: Record<string, string>;
69
+ }>;
70
+ /** Export the current SoulSpec as JSON. Returns file map + agent name. */
71
+ onExportSoul?: () => Promise<{
72
+ files: Record<string, string>;
73
+ name: string;
74
+ }>;
75
+ /** Publish the SoulSpec to the ClawSouls registry. */
76
+ onPublishSoul?: (owner: string) => Promise<void>;
77
+ }
78
+ declare function AgentIdentityTab({ agent, FileTreeEditor, onSaved, onGenerateSoul, onImportSoul, onExportSoul, onPublishSoul, }: AgentIdentityTabProps): react_jsx_runtime.JSX.Element;
79
+
80
+ export { AgentIdentityTab, type AgentIdentityTabProps, FileTreeEditor, type FileTreeEditorProps, type FlatFile, PluginEditorPage, type PluginEditorPageProps };
package/dist/editor.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Badge, Button, Input, useNavigation, useAgentPlaneClient, useApi, Skeleton, Card, CardHeader, CardTitle, CardContent } from './chunk-P4N2P42X.js';
1
+ import { Badge, Button, Input, useNavigation, useAgentPlaneClient, useApi, Skeleton, Card, CardHeader, CardTitle, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, FormField, DialogFooter } from './chunk-OOBDCC6Q.js';
2
2
  import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
3
3
  import CodeMirror from '@uiw/react-codemirror';
4
4
  import { markdown } from '@codemirror/lang-markdown';
@@ -542,5 +542,274 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
542
542
  ] })
543
543
  ] });
544
544
  }
545
+ var FILE_MAP = [
546
+ { path: "SOUL.md", field: "soul_md" },
547
+ { path: "IDENTITY.md", field: "identity_md" },
548
+ { path: "STYLE.md", field: "style_md" },
549
+ { path: "AGENTS.md", field: "agents_md" },
550
+ { path: "HEARTBEAT.md", field: "heartbeat_md" },
551
+ { path: "USER_TEMPLATE.md", field: "user_template_md" },
552
+ { path: "examples/good-outputs.md", field: "examples_good_md" },
553
+ { path: "examples/bad-outputs.md", field: "examples_bad_md" }
554
+ ];
555
+ function agentToFiles(agent) {
556
+ const files = [];
557
+ for (const { path, field } of FILE_MAP) {
558
+ const value = agent[field];
559
+ if (typeof value === "string" && value.length > 0) {
560
+ files.push({ path, content: value });
561
+ }
562
+ }
563
+ return files;
564
+ }
565
+ function filesToPayload(files) {
566
+ const payload = {};
567
+ for (const { path, field } of FILE_MAP) {
568
+ const file = files.find((f) => f.path === path);
569
+ payload[field] = file ? file.content : null;
570
+ }
571
+ return payload;
572
+ }
573
+ function AgentIdentityTab({
574
+ agent,
575
+ FileTreeEditor: FileTreeEditor2,
576
+ onSaved,
577
+ onGenerateSoul,
578
+ onImportSoul,
579
+ onExportSoul,
580
+ onPublishSoul
581
+ }) {
582
+ const client = useAgentPlaneClient();
583
+ const [saving, setSaving] = useState(false);
584
+ const [error, setError] = useState("");
585
+ const [generating, setGenerating] = useState(false);
586
+ const [publishing, setPublishing] = useState(false);
587
+ const [importOpen, setImportOpen] = useState(false);
588
+ const [savedVersion, setSavedVersion] = useState(0);
589
+ const [overrideFiles, setOverrideFiles] = useState(null);
590
+ const initialFiles = useMemo(() => agentToFiles(agent), [agent]);
591
+ const editorFiles = overrideFiles ?? initialFiles;
592
+ const handleSave = useCallback(
593
+ async (files) => {
594
+ setSaving(true);
595
+ setError("");
596
+ try {
597
+ const payload = filesToPayload(files);
598
+ await client.agents.update(agent.id, payload);
599
+ setOverrideFiles(null);
600
+ setSavedVersion((v) => v + 1);
601
+ onSaved?.();
602
+ } catch (err) {
603
+ setError(err instanceof Error ? err.message : "Failed to save");
604
+ throw err;
605
+ } finally {
606
+ setSaving(false);
607
+ }
608
+ },
609
+ [agent.id, client, onSaved]
610
+ );
611
+ function applyFilesFromResponse(responseFiles) {
612
+ const newFiles = [];
613
+ for (const { path, field } of FILE_MAP) {
614
+ const content = responseFiles[field] ?? responseFiles[path];
615
+ if (content) {
616
+ newFiles.push({ path, content });
617
+ }
618
+ }
619
+ if (newFiles.length > 0) {
620
+ setOverrideFiles(newFiles);
621
+ }
622
+ }
623
+ async function handleGenerate() {
624
+ if (!onGenerateSoul) return;
625
+ setGenerating(true);
626
+ setError("");
627
+ try {
628
+ const data = await onGenerateSoul();
629
+ applyFilesFromResponse(data.files);
630
+ } catch (err) {
631
+ setError(err instanceof Error ? err.message : "Failed to generate");
632
+ } finally {
633
+ setGenerating(false);
634
+ }
635
+ }
636
+ function handleImported(responseFiles) {
637
+ applyFilesFromResponse(responseFiles);
638
+ }
639
+ async function handleExport() {
640
+ if (!onExportSoul) return;
641
+ setError("");
642
+ try {
643
+ const data = await onExportSoul();
644
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
645
+ type: "application/json"
646
+ });
647
+ const url = URL.createObjectURL(blob);
648
+ const a = document.createElement("a");
649
+ a.href = url;
650
+ a.download = `${data.name || "soulspec"}.json`;
651
+ a.click();
652
+ URL.revokeObjectURL(url);
653
+ } catch (err) {
654
+ setError(err instanceof Error ? err.message : "Failed to export");
655
+ }
656
+ }
657
+ async function handlePublish() {
658
+ if (!onPublishSoul) return;
659
+ const owner = prompt("Enter owner name for publishing:");
660
+ if (!owner?.trim()) return;
661
+ setPublishing(true);
662
+ setError("");
663
+ try {
664
+ await onPublishSoul(owner.trim());
665
+ } catch (err) {
666
+ setError(err instanceof Error ? err.message : "Failed to publish");
667
+ } finally {
668
+ setPublishing(false);
669
+ }
670
+ }
671
+ const warnings = [];
672
+ const hasSoul = editorFiles.some(
673
+ (f) => f.path === "SOUL.md" && f.content.trim().length > 0
674
+ );
675
+ const hasIdentity = editorFiles.some(
676
+ (f) => f.path === "IDENTITY.md" && f.content.trim().length > 0
677
+ );
678
+ if (!hasSoul) warnings.push("SOUL.md is empty -- this is the core identity file");
679
+ if (!hasIdentity)
680
+ warnings.push("IDENTITY.md is empty -- consider adding behavioral traits");
681
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
682
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
683
+ onGenerateSoul && /* @__PURE__ */ jsx(
684
+ Button,
685
+ {
686
+ variant: "outline",
687
+ size: "sm",
688
+ onClick: handleGenerate,
689
+ disabled: generating,
690
+ children: generating ? "Generating..." : "Generate Soul"
691
+ }
692
+ ),
693
+ onImportSoul && /* @__PURE__ */ jsx(
694
+ Button,
695
+ {
696
+ variant: "outline",
697
+ size: "sm",
698
+ onClick: () => setImportOpen(true),
699
+ children: "Import"
700
+ }
701
+ ),
702
+ onExportSoul && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleExport, children: "Export" }),
703
+ onPublishSoul && /* @__PURE__ */ jsx(
704
+ Button,
705
+ {
706
+ variant: "outline",
707
+ size: "sm",
708
+ onClick: handlePublish,
709
+ disabled: publishing,
710
+ children: publishing ? "Publishing..." : "Publish"
711
+ }
712
+ ),
713
+ overrideFiles && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved generated content" })
714
+ ] }),
715
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
716
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: /* @__PURE__ */ jsx(
717
+ FileTreeEditor2,
718
+ {
719
+ initialFiles: editorFiles,
720
+ onSave: handleSave,
721
+ title: "SoulSpec",
722
+ saveLabel: saving ? "Saving..." : "Save Identity",
723
+ addFolderLabel: "Folder",
724
+ newFileTemplate: {
725
+ filename: "CUSTOM.md",
726
+ content: "# Custom\n\nAdd custom identity content...\n"
727
+ },
728
+ savedVersion
729
+ }
730
+ ) }),
731
+ warnings.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-amber-600/30 bg-amber-950/20 p-3", children: [
732
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-amber-500 mb-1", children: "Validation Warnings" }),
733
+ /* @__PURE__ */ jsx("ul", { className: "text-xs text-amber-400/80 space-y-0.5", children: warnings.map((w) => /* @__PURE__ */ jsxs("li", { children: [
734
+ "- ",
735
+ w
736
+ ] }, w)) })
737
+ ] }),
738
+ onImportSoul && /* @__PURE__ */ jsx(
739
+ ImportSoulDialog,
740
+ {
741
+ open: importOpen,
742
+ onOpenChange: setImportOpen,
743
+ onImport: onImportSoul,
744
+ onImported: handleImported
745
+ }
746
+ )
747
+ ] });
748
+ }
749
+ function ImportSoulDialog({
750
+ open,
751
+ onOpenChange,
752
+ onImport,
753
+ onImported
754
+ }) {
755
+ const [ref, setRef] = useState("");
756
+ const [loading, setLoading] = useState(false);
757
+ const [error, setError] = useState("");
758
+ async function handleImport() {
759
+ if (!ref.trim()) return;
760
+ setLoading(true);
761
+ setError("");
762
+ try {
763
+ const data = await onImport(ref.trim());
764
+ onImported(data.files);
765
+ onOpenChange(false);
766
+ setRef("");
767
+ } catch (err) {
768
+ setError(err instanceof Error ? err.message : "Failed to import");
769
+ } finally {
770
+ setLoading(false);
771
+ }
772
+ }
773
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-md", children: [
774
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
775
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Import SoulSpec" }),
776
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Import a SoulSpec from the ClawSouls registry." })
777
+ ] }),
778
+ /* @__PURE__ */ jsxs(DialogBody, { children: [
779
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive mb-3", children: error }),
780
+ /* @__PURE__ */ jsx(FormField, { label: "Registry Reference", children: /* @__PURE__ */ jsx(
781
+ Input,
782
+ {
783
+ value: ref,
784
+ onChange: (e) => setRef(e.target.value),
785
+ placeholder: "owner/name (e.g. clawsouls/surgical-coder)",
786
+ onKeyDown: (e) => e.key === "Enter" && handleImport(),
787
+ autoFocus: true
788
+ }
789
+ ) })
790
+ ] }),
791
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
792
+ /* @__PURE__ */ jsx(
793
+ Button,
794
+ {
795
+ variant: "outline",
796
+ size: "sm",
797
+ onClick: () => onOpenChange(false),
798
+ disabled: loading,
799
+ children: "Cancel"
800
+ }
801
+ ),
802
+ /* @__PURE__ */ jsx(
803
+ Button,
804
+ {
805
+ size: "sm",
806
+ onClick: handleImport,
807
+ disabled: loading || !ref.trim(),
808
+ children: loading ? "Importing..." : "Import"
809
+ }
810
+ )
811
+ ] })
812
+ ] }) });
813
+ }
545
814
 
546
- export { FileTreeEditor, PluginEditorPage };
815
+ export { AgentIdentityTab, FileTreeEditor, PluginEditorPage };