@getcatalystiq/agent-plane-ui 0.1.17 → 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.
@@ -1,6 +1,6 @@
1
1
  import * as React3 from 'react';
2
2
  import { createContext, useRef, useMemo, useContext } from 'react';
3
- import { jsx } from 'react/jsx-runtime';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
4
  import useSWR from 'swr';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
@@ -193,8 +193,81 @@ var Input = React3.forwardRef(
193
193
  }
194
194
  );
195
195
  Input.displayName = "Input";
196
+ function FormField({ label, children, error, hint }) {
197
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
198
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label }),
199
+ children,
200
+ hint && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: hint }),
201
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-1", children: error })
202
+ ] });
203
+ }
196
204
  function Skeleton({ className, ...props }) {
197
205
  return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted/50", className), ...props });
198
206
  }
207
+ function Dialog({ open, onOpenChange, children }) {
208
+ React3.useEffect(() => {
209
+ if (!open) return;
210
+ function onKeyDown(e) {
211
+ if (e.key === "Escape") onOpenChange(false);
212
+ }
213
+ document.addEventListener("keydown", onKeyDown);
214
+ return () => document.removeEventListener("keydown", onKeyDown);
215
+ }, [open, onOpenChange]);
216
+ React3.useEffect(() => {
217
+ if (!open) return;
218
+ const prev = document.body.style.overflow;
219
+ document.body.style.overflow = "hidden";
220
+ return () => {
221
+ document.body.style.overflow = prev;
222
+ };
223
+ }, [open]);
224
+ if (!open) return null;
225
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
226
+ /* @__PURE__ */ jsx(
227
+ "div",
228
+ {
229
+ className: "absolute inset-0 bg-black/60 animate-dialog-overlay",
230
+ onClick: () => onOpenChange(false)
231
+ }
232
+ ),
233
+ children
234
+ ] });
235
+ }
236
+ function DialogContent({ className, children, ...props }) {
237
+ return /* @__PURE__ */ jsx(
238
+ "div",
239
+ {
240
+ className: cn(
241
+ "relative z-10 w-full bg-background border border-border rounded-xl shadow-2xl mx-4 max-h-[85vh] overflow-y-auto animate-dialog-content",
242
+ className
243
+ ),
244
+ onClick: (e) => e.stopPropagation(),
245
+ ...props,
246
+ children
247
+ }
248
+ );
249
+ }
250
+ function DialogHeader({ className, children, ...props }) {
251
+ return /* @__PURE__ */ jsx("div", { className: cn("px-6 pt-6 pb-0", className), ...props, children });
252
+ }
253
+ function DialogBody({ className, children, ...props }) {
254
+ return /* @__PURE__ */ jsx("div", { className: cn("px-6 py-4", className), ...props, children });
255
+ }
256
+ function DialogFooter({ className, children, ...props }) {
257
+ return /* @__PURE__ */ jsx(
258
+ "div",
259
+ {
260
+ className: cn("flex items-center justify-end gap-2 px-6 py-4 border-t border-border bg-muted/30 rounded-b-xl", className),
261
+ ...props,
262
+ children
263
+ }
264
+ );
265
+ }
266
+ function DialogTitle({ className, children, ...props }) {
267
+ return /* @__PURE__ */ jsx("h2", { className: cn("text-base font-semibold", className), ...props, children });
268
+ }
269
+ function DialogDescription({ className, children, ...props }) {
270
+ return /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground mt-1", className), ...props, children });
271
+ }
199
272
 
200
- export { AgentPlaneProvider, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Skeleton, badgeVariants, buttonVariants, cn, supportsClaudeRunner, useAgentPlaneClient, useApi, useAuthError, useNavigation };
273
+ export { AgentPlaneProvider, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FormField, Input, Skeleton, badgeVariants, buttonVariants, cn, supportsClaudeRunner, useAgentPlaneClient, useApi, useAuthError, useNavigation };
@@ -217,9 +217,82 @@ var Input = React3__namespace.forwardRef(
217
217
  }
218
218
  );
219
219
  Input.displayName = "Input";
220
+ function FormField({ label, children, error, hint }) {
221
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
222
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label }),
223
+ children,
224
+ hint && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: hint }),
225
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive mt-1", children: error })
226
+ ] });
227
+ }
220
228
  function Skeleton({ className, ...props }) {
221
229
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("animate-pulse rounded-md bg-muted/50", className), ...props });
222
230
  }
231
+ function Dialog({ open, onOpenChange, children }) {
232
+ React3__namespace.useEffect(() => {
233
+ if (!open) return;
234
+ function onKeyDown(e) {
235
+ if (e.key === "Escape") onOpenChange(false);
236
+ }
237
+ document.addEventListener("keydown", onKeyDown);
238
+ return () => document.removeEventListener("keydown", onKeyDown);
239
+ }, [open, onOpenChange]);
240
+ React3__namespace.useEffect(() => {
241
+ if (!open) return;
242
+ const prev = document.body.style.overflow;
243
+ document.body.style.overflow = "hidden";
244
+ return () => {
245
+ document.body.style.overflow = prev;
246
+ };
247
+ }, [open]);
248
+ if (!open) return null;
249
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
250
+ /* @__PURE__ */ jsxRuntime.jsx(
251
+ "div",
252
+ {
253
+ className: "absolute inset-0 bg-black/60 animate-dialog-overlay",
254
+ onClick: () => onOpenChange(false)
255
+ }
256
+ ),
257
+ children
258
+ ] });
259
+ }
260
+ function DialogContent({ className, children, ...props }) {
261
+ return /* @__PURE__ */ jsxRuntime.jsx(
262
+ "div",
263
+ {
264
+ className: cn(
265
+ "relative z-10 w-full bg-background border border-border rounded-xl shadow-2xl mx-4 max-h-[85vh] overflow-y-auto animate-dialog-content",
266
+ className
267
+ ),
268
+ onClick: (e) => e.stopPropagation(),
269
+ ...props,
270
+ children
271
+ }
272
+ );
273
+ }
274
+ function DialogHeader({ className, children, ...props }) {
275
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-6 pt-6 pb-0", className), ...props, children });
276
+ }
277
+ function DialogBody({ className, children, ...props }) {
278
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-6 py-4", className), ...props, children });
279
+ }
280
+ function DialogFooter({ className, children, ...props }) {
281
+ return /* @__PURE__ */ jsxRuntime.jsx(
282
+ "div",
283
+ {
284
+ className: cn("flex items-center justify-end gap-2 px-6 py-4 border-t border-border bg-muted/30 rounded-b-xl", className),
285
+ ...props,
286
+ children
287
+ }
288
+ );
289
+ }
290
+ function DialogTitle({ className, children, ...props }) {
291
+ return /* @__PURE__ */ jsxRuntime.jsx("h2", { className: cn("text-base font-semibold", className), ...props, children });
292
+ }
293
+ function DialogDescription({ className, children, ...props }) {
294
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-sm text-muted-foreground mt-1", className), ...props, children });
295
+ }
223
296
 
224
297
  exports.AgentPlaneProvider = AgentPlaneProvider;
225
298
  exports.Badge = Badge;
@@ -229,6 +302,14 @@ exports.CardContent = CardContent;
229
302
  exports.CardDescription = CardDescription;
230
303
  exports.CardHeader = CardHeader;
231
304
  exports.CardTitle = CardTitle;
305
+ exports.Dialog = Dialog;
306
+ exports.DialogBody = DialogBody;
307
+ exports.DialogContent = DialogContent;
308
+ exports.DialogDescription = DialogDescription;
309
+ exports.DialogFooter = DialogFooter;
310
+ exports.DialogHeader = DialogHeader;
311
+ exports.DialogTitle = DialogTitle;
312
+ exports.FormField = FormField;
232
313
  exports.Input = Input;
233
314
  exports.Skeleton = Skeleton;
234
315
  exports.badgeVariants = badgeVariants;
package/dist/editor.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk4XBBDUSZ_cjs = require('./chunk-4XBBDUSZ.cjs');
3
+ var chunkVBGYWQUF_cjs = require('./chunk-VBGYWQUF.cjs');
4
4
  var react = require('react');
5
5
  var CodeMirror = require('@uiw/react-codemirror');
6
6
  var langMarkdown = require('@codemirror/lang-markdown');
@@ -263,7 +263,7 @@ function FileTreeEditor({
263
263
  }),
264
264
  !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: [
265
265
  /* @__PURE__ */ jsxRuntime.jsx(
266
- chunk4XBBDUSZ_cjs.Input,
266
+ chunkVBGYWQUF_cjs.Input,
267
267
  {
268
268
  value: newFileName,
269
269
  onChange: (e) => setNewFileName(e.target.value),
@@ -273,7 +273,7 @@ function FileTreeEditor({
273
273
  autoFocus: true
274
274
  }
275
275
  ),
276
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
276
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
277
277
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(
278
278
  "button",
279
279
  {
@@ -292,10 +292,10 @@ function FileTreeEditor({
292
292
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
293
293
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
294
294
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: title }),
295
- isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
296
- readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
295
+ isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
296
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
297
297
  ] }),
298
- !readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
298
+ !readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
299
299
  ] }),
300
300
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
301
301
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
@@ -315,7 +315,7 @@ function FileTreeEditor({
315
315
  ] }),
316
316
  showAddFolder && !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
317
317
  /* @__PURE__ */ jsxRuntime.jsx(
318
- chunk4XBBDUSZ_cjs.Input,
318
+ chunkVBGYWQUF_cjs.Input,
319
319
  {
320
320
  value: newFolderName,
321
321
  onChange: (e) => setNewFolderName(e.target.value),
@@ -325,7 +325,7 @@ function FileTreeEditor({
325
325
  autoFocus: true
326
326
  }
327
327
  ),
328
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
328
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
329
329
  ] }),
330
330
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm overflow-y-auto", children: [
331
331
  tree.rootFiles.map((file) => /* @__PURE__ */ jsxRuntime.jsxs(
@@ -382,9 +382,9 @@ function FileTreeEditor({
382
382
  ] });
383
383
  }
384
384
  function PluginEditorPage({ marketplaceId, pluginName }) {
385
- const { LinkComponent, basePath } = chunk4XBBDUSZ_cjs.useNavigation();
386
- const client = chunk4XBBDUSZ_cjs.useAgentPlaneClient();
387
- const { data: pluginFiles, error, isLoading } = chunk4XBBDUSZ_cjs.useApi(
385
+ const { LinkComponent, basePath } = chunkVBGYWQUF_cjs.useNavigation();
386
+ const client = chunkVBGYWQUF_cjs.useAgentPlaneClient();
387
+ const { data: pluginFiles, error, isLoading } = chunkVBGYWQUF_cjs.useApi(
388
388
  `marketplace-${marketplaceId}-plugin-files-${pluginName}`,
389
389
  (c) => c.pluginMarketplaces.getPluginFiles(marketplaceId, pluginName)
390
390
  );
@@ -442,9 +442,9 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
442
442
  }
443
443
  if (isLoading || !pluginFiles) {
444
444
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
445
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-8 w-48" }),
446
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-4 w-96" }),
447
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-[500px] rounded-lg" })
445
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Skeleton, { className: "h-8 w-48" }),
446
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Skeleton, { className: "h-4 w-96" }),
447
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Skeleton, { className: "h-[500px] rounded-lg" })
448
448
  ] });
449
449
  }
450
450
  const tabs = [
@@ -464,7 +464,7 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
464
464
  ) }),
465
465
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
466
466
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold", children: pluginName }),
467
- readOnly ? /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Badge, { variant: "outline", children: "Read-only" }) : /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Badge, { variant: "secondary", children: "Editable" })
467
+ readOnly ? /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "outline", children: "Read-only" }) : /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "secondary", children: "Editable" })
468
468
  ] })
469
469
  ] }),
470
470
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end border-b border-border", children: [
@@ -487,7 +487,7 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
487
487
  !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 ml-auto pb-2", children: [
488
488
  saveError && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: saveError }),
489
489
  success && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-green-500", children: success }),
490
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { size: "sm", onClick: handleSaveAll, disabled: saving, children: saving ? "Pushing to GitHub..." : "Save All to GitHub" })
490
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Button, { size: "sm", onClick: handleSaveAll, disabled: saving, children: saving ? "Pushing to GitHub..." : "Save All to GitHub" })
491
491
  ] })
492
492
  ] }),
493
493
  activeTab === "agents" && /* @__PURE__ */ jsxRuntime.jsx(
@@ -518,12 +518,12 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
518
518
  savedVersion
519
519
  }
520
520
  ),
521
- activeTab === "connectors" && /* @__PURE__ */ jsxRuntime.jsxs(chunk4XBBDUSZ_cjs.Card, { children: [
522
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.CardHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
523
- /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.CardTitle, { className: "text-base", children: "Connectors (.mcp.json)" }),
524
- readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
521
+ activeTab === "connectors" && /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.Card, { children: [
522
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.CardHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
523
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.CardTitle, { className: "text-base", children: "Connectors (.mcp.json)" }),
524
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
525
525
  ] }) }),
526
- /* @__PURE__ */ jsxRuntime.jsxs(chunk4XBBDUSZ_cjs.CardContent, { children: [
526
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.CardContent, { children: [
527
527
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border border-border rounded-md overflow-hidden", children: [
528
528
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: ".mcp.json" }),
529
529
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -548,6 +548,276 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
548
548
  ] })
549
549
  ] });
550
550
  }
551
+ var FILE_MAP = [
552
+ { path: "SOUL.md", field: "soul_md" },
553
+ { path: "IDENTITY.md", field: "identity_md" },
554
+ { path: "STYLE.md", field: "style_md" },
555
+ { path: "AGENTS.md", field: "agents_md" },
556
+ { path: "HEARTBEAT.md", field: "heartbeat_md" },
557
+ { path: "USER_TEMPLATE.md", field: "user_template_md" },
558
+ { path: "examples/good-outputs.md", field: "examples_good_md" },
559
+ { path: "examples/bad-outputs.md", field: "examples_bad_md" }
560
+ ];
561
+ function agentToFiles(agent) {
562
+ const files = [];
563
+ for (const { path, field } of FILE_MAP) {
564
+ const value = agent[field];
565
+ if (typeof value === "string" && value.length > 0) {
566
+ files.push({ path, content: value });
567
+ }
568
+ }
569
+ return files;
570
+ }
571
+ function filesToPayload(files) {
572
+ const payload = {};
573
+ for (const { path, field } of FILE_MAP) {
574
+ const file = files.find((f) => f.path === path);
575
+ payload[field] = file ? file.content : null;
576
+ }
577
+ return payload;
578
+ }
579
+ function AgentIdentityTab({
580
+ agent,
581
+ FileTreeEditor: FileTreeEditor2,
582
+ onSaved,
583
+ onGenerateSoul,
584
+ onImportSoul,
585
+ onExportSoul,
586
+ onPublishSoul
587
+ }) {
588
+ const client = chunkVBGYWQUF_cjs.useAgentPlaneClient();
589
+ const [saving, setSaving] = react.useState(false);
590
+ const [error, setError] = react.useState("");
591
+ const [generating, setGenerating] = react.useState(false);
592
+ const [publishing, setPublishing] = react.useState(false);
593
+ const [importOpen, setImportOpen] = react.useState(false);
594
+ const [savedVersion, setSavedVersion] = react.useState(0);
595
+ const [overrideFiles, setOverrideFiles] = react.useState(null);
596
+ const initialFiles = react.useMemo(() => agentToFiles(agent), [agent]);
597
+ const editorFiles = overrideFiles ?? initialFiles;
598
+ const handleSave = react.useCallback(
599
+ async (files) => {
600
+ setSaving(true);
601
+ setError("");
602
+ try {
603
+ const payload = filesToPayload(files);
604
+ await client.agents.update(agent.id, payload);
605
+ setOverrideFiles(null);
606
+ setSavedVersion((v) => v + 1);
607
+ onSaved?.();
608
+ } catch (err) {
609
+ setError(err instanceof Error ? err.message : "Failed to save");
610
+ throw err;
611
+ } finally {
612
+ setSaving(false);
613
+ }
614
+ },
615
+ [agent.id, client, onSaved]
616
+ );
617
+ function applyFilesFromResponse(responseFiles) {
618
+ const newFiles = [];
619
+ for (const { path, field } of FILE_MAP) {
620
+ const content = responseFiles[field] ?? responseFiles[path];
621
+ if (content) {
622
+ newFiles.push({ path, content });
623
+ }
624
+ }
625
+ if (newFiles.length > 0) {
626
+ setOverrideFiles(newFiles);
627
+ }
628
+ }
629
+ async function handleGenerate() {
630
+ if (!onGenerateSoul) return;
631
+ setGenerating(true);
632
+ setError("");
633
+ try {
634
+ const data = await onGenerateSoul();
635
+ applyFilesFromResponse(data.files);
636
+ } catch (err) {
637
+ setError(err instanceof Error ? err.message : "Failed to generate");
638
+ } finally {
639
+ setGenerating(false);
640
+ }
641
+ }
642
+ function handleImported(responseFiles) {
643
+ applyFilesFromResponse(responseFiles);
644
+ }
645
+ async function handleExport() {
646
+ if (!onExportSoul) return;
647
+ setError("");
648
+ try {
649
+ const data = await onExportSoul();
650
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
651
+ type: "application/json"
652
+ });
653
+ const url = URL.createObjectURL(blob);
654
+ const a = document.createElement("a");
655
+ a.href = url;
656
+ a.download = `${data.name || "soulspec"}.json`;
657
+ a.click();
658
+ URL.revokeObjectURL(url);
659
+ } catch (err) {
660
+ setError(err instanceof Error ? err.message : "Failed to export");
661
+ }
662
+ }
663
+ async function handlePublish() {
664
+ if (!onPublishSoul) return;
665
+ const owner = prompt("Enter owner name for publishing:");
666
+ if (!owner?.trim()) return;
667
+ setPublishing(true);
668
+ setError("");
669
+ try {
670
+ await onPublishSoul(owner.trim());
671
+ } catch (err) {
672
+ setError(err instanceof Error ? err.message : "Failed to publish");
673
+ } finally {
674
+ setPublishing(false);
675
+ }
676
+ }
677
+ const warnings = [];
678
+ const hasSoul = editorFiles.some(
679
+ (f) => f.path === "SOUL.md" && f.content.trim().length > 0
680
+ );
681
+ const hasIdentity = editorFiles.some(
682
+ (f) => f.path === "IDENTITY.md" && f.content.trim().length > 0
683
+ );
684
+ if (!hasSoul) warnings.push("SOUL.md is empty -- this is the core identity file");
685
+ if (!hasIdentity)
686
+ warnings.push("IDENTITY.md is empty -- consider adding behavioral traits");
687
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
688
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
689
+ onGenerateSoul && /* @__PURE__ */ jsxRuntime.jsx(
690
+ chunkVBGYWQUF_cjs.Button,
691
+ {
692
+ variant: "outline",
693
+ size: "sm",
694
+ onClick: handleGenerate,
695
+ disabled: generating,
696
+ children: generating ? "Generating..." : "Generate Soul"
697
+ }
698
+ ),
699
+ onImportSoul && /* @__PURE__ */ jsxRuntime.jsx(
700
+ chunkVBGYWQUF_cjs.Button,
701
+ {
702
+ variant: "outline",
703
+ size: "sm",
704
+ onClick: () => setImportOpen(true),
705
+ children: "Import"
706
+ }
707
+ ),
708
+ onExportSoul && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Button, { variant: "outline", size: "sm", onClick: handleExport, children: "Export" }),
709
+ onPublishSoul && /* @__PURE__ */ jsxRuntime.jsx(
710
+ chunkVBGYWQUF_cjs.Button,
711
+ {
712
+ variant: "outline",
713
+ size: "sm",
714
+ onClick: handlePublish,
715
+ disabled: publishing,
716
+ children: publishing ? "Publishing..." : "Publish"
717
+ }
718
+ ),
719
+ overrideFiles && /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Badge, { variant: "destructive", className: "text-xs", children: "Unsaved generated content" })
720
+ ] }),
721
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
722
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: /* @__PURE__ */ jsxRuntime.jsx(
723
+ FileTreeEditor2,
724
+ {
725
+ initialFiles: editorFiles,
726
+ onSave: handleSave,
727
+ title: "SoulSpec",
728
+ saveLabel: saving ? "Saving..." : "Save Identity",
729
+ addFolderLabel: "Folder",
730
+ newFileTemplate: {
731
+ filename: "CUSTOM.md",
732
+ content: "# Custom\n\nAdd custom identity content...\n"
733
+ },
734
+ savedVersion
735
+ }
736
+ ) }),
737
+ warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-amber-600/30 bg-amber-950/20 p-3", children: [
738
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-amber-500 mb-1", children: "Validation Warnings" }),
739
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "text-xs text-amber-400/80 space-y-0.5", children: warnings.map((w) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
740
+ "- ",
741
+ w
742
+ ] }, w)) })
743
+ ] }),
744
+ onImportSoul && /* @__PURE__ */ jsxRuntime.jsx(
745
+ ImportSoulDialog,
746
+ {
747
+ open: importOpen,
748
+ onOpenChange: setImportOpen,
749
+ onImport: onImportSoul,
750
+ onImported: handleImported
751
+ }
752
+ )
753
+ ] });
754
+ }
755
+ function ImportSoulDialog({
756
+ open,
757
+ onOpenChange,
758
+ onImport,
759
+ onImported
760
+ }) {
761
+ const [ref, setRef] = react.useState("");
762
+ const [loading, setLoading] = react.useState(false);
763
+ const [error, setError] = react.useState("");
764
+ async function handleImport() {
765
+ if (!ref.trim()) return;
766
+ setLoading(true);
767
+ setError("");
768
+ try {
769
+ const data = await onImport(ref.trim());
770
+ onImported(data.files);
771
+ onOpenChange(false);
772
+ setRef("");
773
+ } catch (err) {
774
+ setError(err instanceof Error ? err.message : "Failed to import");
775
+ } finally {
776
+ setLoading(false);
777
+ }
778
+ }
779
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.DialogContent, { className: "max-w-md", children: [
780
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.DialogHeader, { children: [
781
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.DialogTitle, { children: "Import SoulSpec" }),
782
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.DialogDescription, { children: "Import a SoulSpec from the ClawSouls registry." })
783
+ ] }),
784
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.DialogBody, { children: [
785
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive mb-3", children: error }),
786
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVBGYWQUF_cjs.FormField, { label: "Registry Reference", children: /* @__PURE__ */ jsxRuntime.jsx(
787
+ chunkVBGYWQUF_cjs.Input,
788
+ {
789
+ value: ref,
790
+ onChange: (e) => setRef(e.target.value),
791
+ placeholder: "owner/name (e.g. clawsouls/surgical-coder)",
792
+ onKeyDown: (e) => e.key === "Enter" && handleImport(),
793
+ autoFocus: true
794
+ }
795
+ ) })
796
+ ] }),
797
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkVBGYWQUF_cjs.DialogFooter, { children: [
798
+ /* @__PURE__ */ jsxRuntime.jsx(
799
+ chunkVBGYWQUF_cjs.Button,
800
+ {
801
+ variant: "outline",
802
+ size: "sm",
803
+ onClick: () => onOpenChange(false),
804
+ disabled: loading,
805
+ children: "Cancel"
806
+ }
807
+ ),
808
+ /* @__PURE__ */ jsxRuntime.jsx(
809
+ chunkVBGYWQUF_cjs.Button,
810
+ {
811
+ size: "sm",
812
+ onClick: handleImport,
813
+ disabled: loading || !ref.trim(),
814
+ children: loading ? "Importing..." : "Import"
815
+ }
816
+ )
817
+ ] })
818
+ ] }) });
819
+ }
551
820
 
821
+ exports.AgentIdentityTab = AgentIdentityTab;
552
822
  exports.FileTreeEditor = FileTreeEditor;
553
823
  exports.PluginEditorPage = PluginEditorPage;
package/dist/editor.d.cts 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 };