@polpo-ai/dashboard 0.1.2 → 0.1.4

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,21 +1,17 @@
1
1
  "use client";
2
2
 
3
- import { useState } from "react";
3
+ import { useMemo, useState } from "react";
4
4
  import { useParams } from "next/navigation";
5
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
6
- import {
7
- ArrowLeft,
8
- BookMarked,
9
- Check,
10
- Download,
11
- FilePlus2,
12
- Link2,
13
- Loader2,
14
- Search,
15
- } from "lucide-react";
5
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
6
+ import { ArrowLeft, Check, Download, FilePlus2, Link2, Loader2 } from "lucide-react";
16
7
  import { usePolpoClient } from "../../lib/polpo-client";
17
- import { usePolpoSkillsAdapter } from "@lumea-labs/skills-polpo";
18
- import { SkillsProvider, SkillCreateForm } from "@lumea-labs/skills";
8
+ import {
9
+ SkillsProvider,
10
+ SkillCreateForm,
11
+ type SkillsAdapter,
12
+ type SkillCreateInput,
13
+ type Skill,
14
+ } from "@lumea-labs/skills";
19
15
  import {
20
16
  Dialog,
21
17
  DialogContent,
@@ -26,49 +22,20 @@ import {
26
22
  } from "../../components/ui/dialog";
27
23
 
28
24
  /**
29
- * SkillsInstallWizard — replaces the old read-only "how to install" dialog
30
- * with a real, guided stepper that ACTUALLY installs skills, dogfooding the
31
- * public SDK end to end:
32
- * - registry browsethe Mastra `skills-api` / skills.sh registry (the
33
- * browse surface Polpo itself doesn't expose) → client.installSkills(url)
34
- * - from a URL → client.installSkills(githubUrl)
35
- * - paste / create → @lumea-labs/skills <SkillCreateForm> over the
36
- * Polpo-wired adapter (adapter.createSkill)
25
+ * SkillsInstallWizard — guided stepper that actually installs skills, dogfooding
26
+ * the SDK end to end:
27
+ * - From a URL → client.installSkills(githubUrl)
28
+ * - Paste/create@lumea-labs/skills <SkillCreateForm> over a minimal
29
+ * SkillsAdapter built on the PolpoClient (adapter.createSkill).
37
30
  *
38
- * The wizard lives inside <SkillsProvider adapter={polpoAdapter}> so the
39
- * lumea-agents skills UI (SkillCreateForm here, more later) is wired to Polpo.
31
+ * (Registry browse via Mastra's @mastra/skills-api is a future addition — it's a
32
+ * Node lib with bundled data, so it needs a server action, not a browser fetch.)
40
33
  */
41
34
 
42
- const SKILLS_API = "https://skills.sh/api/skills";
43
-
44
- type Source = "registry" | "url" | "paste";
45
-
46
- interface RegistrySkill {
47
- id?: string;
48
- name: string;
49
- description?: string;
50
- owner?: string;
51
- repo?: string;
52
- repository?: string;
53
- repositoryUrl?: string;
54
- installs?: number;
55
- }
56
-
57
- /** Best-effort GitHub source for a skills.sh registry record. */
58
- function sourceFor(s: RegistrySkill): string {
59
- if (s.repositoryUrl) return s.repositoryUrl;
60
- if (s.repository?.startsWith("http")) return s.repository;
61
- if (s.owner && s.repo) return `https://github.com/${s.owner}/${s.repo}`;
62
- return s.repository ?? s.name;
63
- }
35
+ type Source = "url" | "paste";
64
36
 
65
37
  export function SkillsInstallWizard() {
66
- const { adapter } = usePolpoSkillsAdapter();
67
- return (
68
- <SkillsProvider adapter={adapter}>
69
- <WizardDialog />
70
- </SkillsProvider>
71
- );
38
+ return <WizardDialog />;
72
39
  }
73
40
 
74
41
  function WizardDialog() {
@@ -78,33 +45,25 @@ function WizardDialog() {
78
45
 
79
46
  const [open, setOpen] = useState(false);
80
47
  const [source, setSource] = useState<Source | null>(null);
81
- const [search, setSearch] = useState("");
82
48
  const [url, setUrl] = useState("");
83
49
  const [done, setDone] = useState<string | null>(null);
84
50
 
85
- const reset = () => {
86
- setSource(null);
87
- setSearch("");
88
- setUrl("");
89
- setDone(null);
90
- install.reset();
91
- };
92
-
93
- const { data: registry = [], isFetching, error: regError } = useQuery({
94
- queryKey: ["skills-registry", search],
95
- queryFn: async (): Promise<RegistrySkill[]> => {
96
- const u = new URL(SKILLS_API);
97
- u.searchParams.set("sortBy", "installs");
98
- u.searchParams.set("pageSize", "30");
99
- if (search.trim()) u.searchParams.set("query", search.trim());
100
- const res = await fetch(u.toString());
101
- if (!res.ok) throw new Error(`registry ${res.status}`);
102
- const json = await res.json();
103
- return (json.skills ?? json.data ?? json.results ?? json) as RegistrySkill[];
104
- },
105
- enabled: open && source === "registry",
106
- staleTime: 60_000,
107
- });
51
+ // Minimal SkillsAdapter on the PolpoClient (same de-dup seam usePolpoClient
52
+ // resolves: OSS direct, cloud session-proxy). Only createSkill is needed.
53
+ const adapter = useMemo<SkillsAdapter>(
54
+ () => ({
55
+ createSkill: async (input: SkillCreateInput): Promise<Skill> => {
56
+ const res = await client.createSkill({
57
+ name: input.name,
58
+ description: input.description,
59
+ content: input.body,
60
+ allowedTools: (input.frontmatter as { allowedTools?: string[] })?.allowedTools,
61
+ });
62
+ return { id: res.name, name: res.name, description: input.description, installed: true } as Skill;
63
+ },
64
+ }),
65
+ [client],
66
+ );
108
67
 
109
68
  const install = useMutation({
110
69
  mutationFn: (src: string) => client.installSkills(src),
@@ -114,6 +73,13 @@ function WizardDialog() {
114
73
  },
115
74
  });
116
75
 
76
+ const reset = () => {
77
+ setSource(null);
78
+ setUrl("");
79
+ setDone(null);
80
+ install.reset();
81
+ };
82
+
117
83
  const afterCreate = () => {
118
84
  qc.invalidateQueries({ queryKey: ["skills", id] });
119
85
  setDone("created");
@@ -134,7 +100,6 @@ function WizardDialog() {
134
100
  </DialogDescription>
135
101
  </DialogHeader>
136
102
 
137
- {/* ── Result ─────────────────────────────────────────── */}
138
103
  {done ? (
139
104
  <div className="flex flex-col items-center gap-3 py-8 text-center">
140
105
  <div className="grid h-10 w-10 place-items-center rounded-full bg-emerald-500/15 text-emerald-500">
@@ -152,14 +117,7 @@ function WizardDialog() {
152
117
  </div>
153
118
  </div>
154
119
  ) : !source ? (
155
- /* ── Step 1: choose a source ──────────────────────── */
156
120
  <div className="grid gap-2 pt-1">
157
- <SourceCard
158
- icon={<BookMarked className="h-4 w-4" />}
159
- title="Browse the registry"
160
- desc="34k+ skills from skills.sh — search and install."
161
- onClick={() => setSource("registry")}
162
- />
163
121
  <SourceCard
164
122
  icon={<Link2 className="h-4 w-4" />}
165
123
  title="From a URL"
@@ -175,49 +133,13 @@ function WizardDialog() {
175
133
  </div>
176
134
  ) : (
177
135
  <div className="flex flex-col gap-3 pt-1">
178
- <button onClick={() => { setSource(null); install.reset(); }} className="inline-flex w-fit items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground">
136
+ <button
137
+ onClick={() => { setSource(null); install.reset(); }}
138
+ className="inline-flex w-fit items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground"
139
+ >
179
140
  <ArrowLeft className="h-3 w-3" /> Back
180
141
  </button>
181
142
 
182
- {/* ── Registry ──────────────────────────────────── */}
183
- {source === "registry" && (
184
- <>
185
- <div className="flex items-center gap-2 border border-border px-2.5">
186
- <Search className="h-3.5 w-3.5 text-muted-foreground" />
187
- <input
188
- value={search}
189
- onChange={(e) => setSearch(e.target.value)}
190
- placeholder="Search skills…"
191
- className="w-full bg-transparent py-2 text-sm outline-none"
192
- />
193
- </div>
194
- {isFetching ? (
195
- <div className="flex items-center gap-2 py-6 text-xs text-muted-foreground"><Loader2 className="h-3.5 w-3.5 animate-spin" /> loading registry…</div>
196
- ) : regError ? (
197
- <p className="py-6 text-center text-xs text-muted-foreground">Registry unavailable. Try the URL option.</p>
198
- ) : (
199
- <ul className="flex max-h-[40vh] flex-col gap-1 overflow-y-auto">
200
- {registry.map((s) => (
201
- <li key={s.id ?? `${s.owner}/${s.repo}/${s.name}`} className="flex items-center justify-between gap-3 border border-border px-3 py-2">
202
- <div className="min-w-0">
203
- <div className="truncate text-sm font-medium">{s.name}</div>
204
- {s.description && <div className="truncate text-[11px] text-muted-foreground">{s.description}</div>}
205
- </div>
206
- <button
207
- disabled={install.isPending}
208
- onClick={() => install.mutate(sourceFor(s))}
209
- className="shrink-0 border border-foreground bg-foreground px-2.5 py-1 text-[11px] text-background hover:bg-foreground/90 disabled:opacity-50"
210
- >
211
- {install.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : "Install"}
212
- </button>
213
- </li>
214
- ))}
215
- </ul>
216
- )}
217
- </>
218
- )}
219
-
220
- {/* ── URL ────────────────────────────────────────── */}
221
143
  {source === "url" && (
222
144
  <form
223
145
  onSubmit={(e) => { e.preventDefault(); if (url.trim()) install.mutate(url.trim()); }}
@@ -242,13 +164,10 @@ function WizardDialog() {
242
164
  </form>
243
165
  )}
244
166
 
245
- {/* ── Paste / create (dogfood lumea-agents UI) ──── */}
246
167
  {source === "paste" && (
247
- <SkillCreateForm onCreated={afterCreate} onCancel={() => setSource(null)} />
248
- )}
249
-
250
- {install.isError && source === "registry" && (
251
- <p className="text-xs text-destructive">{(install.error as Error).message}</p>
168
+ <SkillsProvider adapter={adapter}>
169
+ <SkillCreateForm onCreated={afterCreate} onCancel={() => setSource(null)} />
170
+ </SkillsProvider>
252
171
  )}
253
172
  </div>
254
173
  )}
package/index.ts CHANGED
@@ -12,3 +12,6 @@ export type { PolpoClientFactory } from "./lib/polpo-client";
12
12
 
13
13
  export { default as AgentsView } from "./app/(dashboard)/projects/[id]/agents/view";
14
14
  export type { Team } from "./app/(dashboard)/projects/[id]/agents/view";
15
+
16
+ export { default as SkillsView } from "./app/(dashboard)/projects/[id]/skills/view";
17
+ export type { SkillInfo } from "./app/(dashboard)/projects/[id]/skills/view";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polpo-ai/dashboard",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },