@hanzo/build 0.2.11 → 0.2.12
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/README.md +3 -2
- package/lib/customize/connectors.js +11 -9
- package/lib/customize/index.js +3 -1
- package/lib/customize/skills.js +9 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,8 +113,9 @@ key, a new one shown once, rotated or revoked.
|
|
|
113
113
|
|
|
114
114
|
**Customize.** What the agent brings to a run, a tab each — Skills, Connectors,
|
|
115
115
|
Plugins, Agents — and in each, Yours (what the org has) and Discover (what it
|
|
116
|
-
can add), one search and one Add.
|
|
117
|
-
the
|
|
116
|
+
can add), one search and one Add. Skills and connectors ride into every run in
|
|
117
|
+
the org, so they are an org admin's to add, switch and remove, and a member reads
|
|
118
|
+
them; plugins and agents are any member's.
|
|
118
119
|
|
|
119
120
|
- Skills: the org's own SKILL.md skills, written, revised and deleted here, and
|
|
120
121
|
the brand's catalogue (`/.well-known/agent-skills/`) to read and add. Adding is
|
|
@@ -28,6 +28,8 @@ export function Connectors({ view, q, adding, onAdding, onView }) {
|
|
|
28
28
|
const host = useHost();
|
|
29
29
|
const t = useTarget();
|
|
30
30
|
const signed = Boolean(host.person);
|
|
31
|
+
// What every run in the org connects to is an org admin's to change; a member reads it.
|
|
32
|
+
const may = signed && host.admin;
|
|
31
33
|
const mine = useRead(signed ? () => servers(t) : null, [], [t, signed]);
|
|
32
34
|
const [opened, setOpened] = useState(null);
|
|
33
35
|
const [listing, setListing] = useState(null);
|
|
@@ -43,25 +45,25 @@ export function Connectors({ view, q, adding, onAdding, onView }) {
|
|
|
43
45
|
onView('yours');
|
|
44
46
|
setNote(said);
|
|
45
47
|
} })) : null;
|
|
46
|
-
const detail = listing ? (_jsx(About, { listing: listing, added: mine.value.some((s) => s.listing === listing.id), signed:
|
|
48
|
+
const detail = listing ? (_jsx(About, { listing: listing, added: mine.value.some((s) => s.listing === listing.id), signed: may, onAdd: () => {
|
|
47
49
|
setConnecting(listing);
|
|
48
50
|
setListing(null);
|
|
49
51
|
}, onClose: () => setListing(null) })) : null;
|
|
50
52
|
if (view === 'discover') {
|
|
51
|
-
return (_jsxs(YStack, { gap: "$6", children: [_jsx(Line, { children: note }), _jsx(Shelf, { q: q, signed: signed, added: new Set(mine.value.map((s) => s.listing).filter(Boolean)), onOpen: setListing, onAdd: setConnecting }), _jsx(Natives, { q: q }), detail, connect] }));
|
|
53
|
+
return (_jsxs(YStack, { gap: "$6", children: [_jsx(Line, { children: note }), _jsx(Shelf, { q: q, signed: signed, may: may, added: new Set(mine.value.map((s) => s.listing).filter(Boolean)), onOpen: setListing, onAdd: setConnecting }), _jsx(Natives, { q: q }), detail, connect] }));
|
|
52
54
|
}
|
|
53
55
|
if (!signed) {
|
|
54
56
|
return _jsx(Soft, { action: host.signIn ? _jsx(Button, { size: "sm", onPress: () => host.signIn?.(), children: "Sign in" }) : undefined, children: "Sign in to see your connectors." });
|
|
55
57
|
}
|
|
56
58
|
const shown = mine.value.filter((s) => matches(q, s.name, s.url, s.listing));
|
|
57
|
-
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), mine.error && !mine.value.length ? (_jsx(Soft, { children: mine.error.message })) : mine.loading && !mine.value.length ? (_jsx(Soft, { children: "Reading your connectors\u2026" })) : !mine.value.length ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover connectors" }), children:
|
|
59
|
+
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), mine.error && !mine.value.length ? (_jsx(Soft, { children: mine.error.message })) : mine.loading && !mine.value.length ? (_jsx(Soft, { children: "Reading your connectors\u2026" })) : !mine.value.length ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover connectors" }), children: may ? 'No connectors yet. Add an MCP server by its URL, or pick one off the shelf.' : 'No connectors yet. An org admin adds them.' })) : !shown.length ? (_jsx(Soft, { children: "No connector matches." })) : (_jsx(Grid, { label: "Your connectors", children: shown.map((s) => (_jsx(Tile, { title: s.name, detail: where(s.url), meta: [s.listing ? 'From the shelf' : 'Added by URL', s.secret ? 'secret sealed in KMS' : '', day(s.created)].filter(Boolean).join(' · '), mark: _jsx(Mark, { name: s.name }), onOpen: () => setOpened(s) }, s.id))) })), opened ? (_jsx(Detail, { server: opened, may: may, onClose: () => setOpened(null), onDeleted: () => {
|
|
58
60
|
setNote(`${opened.name} is removed`);
|
|
59
61
|
setOpened(null);
|
|
60
62
|
mine.reload();
|
|
61
63
|
} })) : null, connect] }));
|
|
62
64
|
}
|
|
63
65
|
/** The shelf: featured first, then by name, a page at a time. The platform searches it. */
|
|
64
|
-
function Shelf({ q, signed, added, onOpen, onAdd }) {
|
|
66
|
+
function Shelf({ q, signed, may, added, onOpen, onAdd }) {
|
|
65
67
|
const t = useTarget();
|
|
66
68
|
const host = useHost();
|
|
67
69
|
const text = useSettled(q.trim(), 300);
|
|
@@ -77,7 +79,7 @@ function Shelf({ q, signed, added, onOpen, onAdd }) {
|
|
|
77
79
|
const list = [...(page?.listings ?? []), ...more];
|
|
78
80
|
const hero = !text ? list.find((l) => l.featured) : undefined;
|
|
79
81
|
const rest = hero ? list.filter((l) => l !== hero) : list;
|
|
80
|
-
const action = (l) => ready(l) ? (_jsx(Add, { name: titleOf(l), added: added.has(l.id), onPress: () => onAdd(l) })) : (_jsx(SizableText, { size: "$1", color: "$soft", shrink: 0, pt: "$1.5", children: "Package" }));
|
|
82
|
+
const action = (l) => !may ? undefined : ready(l) ? (_jsx(Add, { name: titleOf(l), added: added.has(l.id), onPress: () => onAdd(l) })) : (_jsx(SizableText, { size: "$1", color: "$soft", shrink: 0, pt: "$1.5", children: "Package" }));
|
|
81
83
|
const next = async () => {
|
|
82
84
|
setPaging(true);
|
|
83
85
|
setNote('');
|
|
@@ -165,7 +167,7 @@ function Connect({ listing, onClose, onAdded }) {
|
|
|
165
167
|
return (_jsxs(Sheet, { title: listing ? `Add ${titleOf(listing)}` : 'Add connector', open: true, onOpenChange: (o) => !o && onClose(), width: 500, children: [listing ? (_jsx(SizableText, { size: "$2", color: "$soft", children: listing.description || listing.name })) : (_jsx(SizableText, { size: "$2", color: "$soft", children: "Any MCP server that speaks HTTP. Its tools join your organization\u2019s and are on once it is added." })), _jsx(Field, { label: "Name", hint: listing ? 'Leave it empty to use the listing’s own.' : undefined, children: _jsx(Input, { value: name, onChangeText: setName, placeholder: listing ? titleOf(listing) : 'Docs', "aria-label": "Name" }) }), listing ? null : (_jsx(Field, { label: "URL", hint: "A public http(s) address. Private and loopback addresses are refused.", children: _jsx(Input, { value: url, onChangeText: setUrl, placeholder: "https://mcp.example.com/mcp", "aria-label": "URL", autoCapitalize: "none" }) })), _jsx(Field, { label: "Secret", hint: "Optional. Sealed in KMS and sent in the header below on every call; never shown again.", children: _jsx(Input, { value: secret, onChangeText: setSecret, placeholder: "Bearer \u2026", "aria-label": "Secret", secureTextEntry: true }) }), secret ? (_jsx(Field, { label: "Header", children: _jsx(Input, { value: header, onChangeText: setHeader, "aria-label": "Header", autoCapitalize: "none" }) })) : null, _jsx(Line, { children: note }), _jsxs(XStack, { gap: "$2", justify: "flex-end", children: [_jsx(Button, { size: "sm", variant: "ghost", onPress: onClose, children: "Cancel" }), _jsx(Button, { size: "sm", disabled: working, onPress: () => void save(), children: "Add" })] })] }));
|
|
166
168
|
}
|
|
167
169
|
/** One of the org's servers: where it is, its secret, its tools and their switches, and removing it. */
|
|
168
|
-
function Detail({ server, onClose, onDeleted }) {
|
|
170
|
+
function Detail({ server, may, onClose, onDeleted }) {
|
|
169
171
|
const t = useTarget();
|
|
170
172
|
const list = useRead(() => tools(t, { source: 'mcp' }), [], [t, server.id]);
|
|
171
173
|
const its = useMemo(() => list.value.filter((x) => owns(server, x.name)), [list.value, server]);
|
|
@@ -187,10 +189,10 @@ function Detail({ server, onClose, onDeleted }) {
|
|
|
187
189
|
setBusy(false);
|
|
188
190
|
}
|
|
189
191
|
};
|
|
190
|
-
return (_jsxs(Sheet, { title: server.name, open: true, onOpenChange: (o) => !o && onClose(), children: [_jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$1", color: "$ink", style: mono, numberOfLines: 2, children: server.url }), _jsxs(XStack, { items: "center", gap: "$1.5", children: [server.secret ? _jsx(KeyRound, { size: 12, opacity: 0.7 }) : _jsx(ServerIcon, { size: 12, opacity: 0.7 }), _jsx(SizableText, { size: "$1", color: "$soft", children: server.secret ? `A secret is sealed in KMS and sent in ${server.header || 'its header'}.` : 'No secret: it is called as it is.' })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(SizableText, { size: "$3", color: "$ink", flex: 1, children: "Tools" }), its.length ? (_jsx(Button, { size: "sm", variant: "outline", disabled: busy, onPress: () => void (off.length ? flip(off, []) : flip([], its.map((x) => x.name))), children: off.length ? 'Turn all on' : 'Turn all off' })) : null] }), list.loading && !list.value.length ? (_jsx(Soft, { children: "Asking the server for its tools\u2026" })) : list.error && !list.value.length ? (_jsx(Soft, { children: list.error.message })) : !its.length ? (_jsx(Soft, { children: "The server listed no tools. One that does not answer is left out until it does." })) : (_jsx(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: "$3", overflow: "hidden", children: its.map((x, i) => {
|
|
192
|
+
return (_jsxs(Sheet, { title: server.name, open: true, onOpenChange: (o) => !o && onClose(), children: [_jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$1", color: "$ink", style: mono, numberOfLines: 2, children: server.url }), _jsxs(XStack, { items: "center", gap: "$1.5", children: [server.secret ? _jsx(KeyRound, { size: 12, opacity: 0.7 }) : _jsx(ServerIcon, { size: 12, opacity: 0.7 }), _jsx(SizableText, { size: "$1", color: "$soft", children: server.secret ? `A secret is sealed in KMS and sent in ${server.header || 'its header'}.` : 'No secret: it is called as it is.' })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(SizableText, { size: "$3", color: "$ink", flex: 1, children: "Tools" }), its.length && may ? (_jsx(Button, { size: "sm", variant: "outline", disabled: busy, onPress: () => void (off.length ? flip(off, []) : flip([], its.map((x) => x.name))), children: off.length ? 'Turn all on' : 'Turn all off' })) : null] }), list.loading && !list.value.length ? (_jsx(Soft, { children: "Asking the server for its tools\u2026" })) : list.error && !list.value.length ? (_jsx(Soft, { children: list.error.message })) : !its.length ? (_jsx(Soft, { children: "The server listed no tools. One that does not answer is left out until it does." })) : (_jsx(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: "$3", overflow: "hidden", children: its.map((x, i) => {
|
|
191
193
|
const short = x.name.slice(server.id.length + 1);
|
|
192
|
-
return (_jsxs(XStack, { items: "center", gap: "$3", px: "$3", py: "$2", borderTopWidth: i ? 1 : 0, borderColor: "$borderColor", children: [_jsxs(YStack, { flex: 1, minW: 0, gap: "$0.5", children: [_jsx(SizableText, { size: "$2", color: "$ink", style: mono, numberOfLines: 1, children: short }), x.description ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 2, children: x.description })) : null] }), _jsx(Switch, { checked: x.activated, disabled: busy, onCheckedChange: (v) => void flip(v ? [x.name] : [], v ? [] : [x.name]), "aria-label": `${short} on` })] }, x.name));
|
|
193
|
-
}) })), _jsx(Line, { children: note }), _jsx(XStack, { justify: "flex-start", children: _jsx(Button, { size: "sm", variant: "ghost", onPress: () => setAsking(true), children: "Remove connector" }) }), _jsx(Confirm, { what: server.name, says: "Its tools leave your organization and every agent, and its sealed secret is destroyed.", open: asking, onOpenChange: setAsking, onYes: async () => {
|
|
194
|
+
return (_jsxs(XStack, { items: "center", gap: "$3", px: "$3", py: "$2", borderTopWidth: i ? 1 : 0, borderColor: "$borderColor", children: [_jsxs(YStack, { flex: 1, minW: 0, gap: "$0.5", children: [_jsx(SizableText, { size: "$2", color: "$ink", style: mono, numberOfLines: 1, children: short }), x.description ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 2, children: x.description })) : null] }), may ? (_jsx(Switch, { checked: x.activated, disabled: busy, onCheckedChange: (v) => void flip(v ? [x.name] : [], v ? [] : [x.name]), "aria-label": `${short} on` })) : (_jsx(SizableText, { size: "$1", color: "$soft", children: x.activated ? 'On' : 'Off' }))] }, x.name));
|
|
195
|
+
}) })), _jsx(Line, { children: note || (may ? '' : 'An org admin adds, switches and removes the organization’s connectors.') }), may ? (_jsx(XStack, { justify: "flex-start", children: _jsx(Button, { size: "sm", variant: "ghost", onPress: () => setAsking(true), children: "Remove connector" }) })) : null, _jsx(Confirm, { what: server.name, says: "Its tools leave your organization and every agent, and its sealed secret is destroyed.", open: asking, onOpenChange: setAsking, onYes: async () => {
|
|
194
196
|
await remove(t, server.id);
|
|
195
197
|
onDeleted();
|
|
196
198
|
} })] }));
|
package/lib/customize/index.js
CHANGED
|
@@ -19,6 +19,8 @@ import { Connectors } from './connectors.js';
|
|
|
19
19
|
import { Plugins } from './plugins.js';
|
|
20
20
|
import { Skills } from './skills.js';
|
|
21
21
|
import { Choice } from './ui.js';
|
|
22
|
+
/** What every run in the org loads: an org admin's to add to, a member's to read. */
|
|
23
|
+
const ORG_KIT = ['skills', 'connectors'];
|
|
22
24
|
const LABEL = {
|
|
23
25
|
skills: { title: 'Skills', add: 'New skill', find: 'Search skills', icon: _jsx(BookOpen, { size: 15 }) },
|
|
24
26
|
connectors: { title: 'Connectors', add: 'Add connector', find: 'Search connectors', icon: _jsx(Plug, { size: 15 }) },
|
|
@@ -46,5 +48,5 @@ export function Customize({ tab, view: first }) {
|
|
|
46
48
|
return (_jsx(YStack, { flex: 1, minH: 0, overflow: "scroll", px: "$6", py: "$6", "$max-md": { px: '$4', py: '$4' }, children: _jsxs(YStack, { width: "100%", maxW: 1080, mx: "auto", gap: "$4", children: [_jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { render: "h1", size: "$6", color: "$ink", children: "Customize" }), _jsx(SizableText, { size: "$2", color: "$soft", children: "What the agent brings to a run: the skills it knows, the connectors it calls, the plugins it runs, and agents of your own." })] }), _jsx(XStack, { role: "tablist", "aria-label": "Customize", gap: "$4", borderBottomWidth: 1, borderColor: "$borderColor", "$max-md": { gap: '$3' }, children: TABS.map((id) => {
|
|
47
49
|
const on = id === tab;
|
|
48
50
|
return (_jsxs(XStack, { role: "tab", render: "button", "aria-selected": on, onPress: () => pick(id), items: "center", gap: "$1.5", pb: "$2", mb: -1, borderBottomWidth: 2, borderColor: on ? '$ink' : 'transparent', hoverStyle: { borderColor: on ? '$ink' : '$edge' }, children: [_jsx(XStack, { opacity: on ? 1 : 0.6, "$max-md": { display: 'none' }, children: LABEL[id].icon }), _jsx(SizableText, { size: "$3", color: on ? '$ink' : '$soft', children: LABEL[id].title })] }, id));
|
|
49
|
-
}) }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Choice, { label: "Show", value: view, options: VIEWS, onChange: setView }), _jsx(XStack, { flex: 1 }), host.person ? (_jsxs(Button, { size: "sm", onPress: () => setAdding(true), children: [_jsx(Plus, { size: 14 }), " ", LABEL[tab].add] })) : null] }), _jsx(Input, { value: q, onChangeText: setQ, placeholder: `${LABEL[tab].find}…`, "aria-label": LABEL[tab].find }), tab === 'skills' ? (_jsx(Skills, { ...pane })) : tab === 'connectors' ? (_jsx(Connectors, { ...pane })) : tab === 'plugins' ? (_jsx(Plugins, { ...pane })) : (_jsx(Agents, { ...pane }))] }) }));
|
|
51
|
+
}) }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Choice, { label: "Show", value: view, options: VIEWS, onChange: setView }), _jsx(XStack, { flex: 1 }), host.person && (host.admin || !ORG_KIT.includes(tab)) ? (_jsxs(Button, { size: "sm", onPress: () => setAdding(true), children: [_jsx(Plus, { size: 14 }), " ", LABEL[tab].add] })) : null] }), _jsx(Input, { value: q, onChangeText: setQ, placeholder: `${LABEL[tab].find}…`, "aria-label": LABEL[tab].find }), tab === 'skills' ? (_jsx(Skills, { ...pane })) : tab === 'connectors' ? (_jsx(Connectors, { ...pane })) : tab === 'plugins' ? (_jsx(Plugins, { ...pane })) : (_jsx(Agents, { ...pane }))] }) }));
|
|
50
52
|
}
|
package/lib/customize/skills.js
CHANGED
|
@@ -23,6 +23,8 @@ export function Skills({ view, q, adding, onAdding, onView }) {
|
|
|
23
23
|
const host = useHost();
|
|
24
24
|
const t = useTarget();
|
|
25
25
|
const signed = Boolean(host.person);
|
|
26
|
+
// What every run in the org loads is an org admin's to change; a member reads it.
|
|
27
|
+
const may = signed && host.admin;
|
|
26
28
|
const own = useRead(signed ? () => authored(t) : null, [], [t, signed]);
|
|
27
29
|
const on = useRead(signed ? () => active(t) : null, [], [t, signed]);
|
|
28
30
|
const shelf = useRead(view === 'discover' ? () => brand(t) : null, null, [t.api, view]);
|
|
@@ -49,8 +51,8 @@ export function Skills({ view, q, adding, onAdding, onView }) {
|
|
|
49
51
|
setBusy('');
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
|
-
const reader = reading ? (_jsx(Reader, { skill: reading, added: lit.has(tool(reading.name)), signed:
|
|
53
|
-
const editor = adding || editing ? (_jsx(Editor, { skill: editing, onClose: () => {
|
|
54
|
+
const reader = reading ? (_jsx(Reader, { skill: reading, added: lit.has(tool(reading.name)), signed: may, busy: busy === reading.name, onFlip: (next) => void flip(reading.name, next), onClose: () => setReading(null) })) : null;
|
|
55
|
+
const editor = adding || editing ? (_jsx(Editor, { skill: editing, may: may, onClose: () => {
|
|
54
56
|
setEditing(null);
|
|
55
57
|
onAdding(false);
|
|
56
58
|
}, onSaved: (s, fresh) => {
|
|
@@ -69,7 +71,7 @@ export function Skills({ view, q, adding, onAdding, onView }) {
|
|
|
69
71
|
if (view === 'discover') {
|
|
70
72
|
const all = shelf.value?.skills ?? [];
|
|
71
73
|
const found = all.filter((e) => matches(q, e.name, e.description, e.product));
|
|
72
|
-
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), shelf.error && !shelf.value ? (_jsx(Soft, { children: shelf.error.message })) : !shelf.value ? (_jsx(Soft, { children: "Reading the catalogue\u2026" })) : !found.length ? (_jsx(Soft, { children: all.length ? 'No skill matches.' : 'The catalogue lists no skills.' })) : (_jsxs(_Fragment, { children: [_jsx(SizableText, { size: "$1", color: "$soft", children: found.length === all.length ? `${all.length} skills across ${shelf.value.products.length} products` : `${found.length} of ${all.length} skills` }), _jsx(Grid, { label: "Skills to add", children: found.slice(0, shown).map((e) => (_jsx(Tile, { title: e.name, detail: e.description, meta: e.product, mark: _jsx(Mark, { name: e.name, icon: _jsx(BookOpen, { size: 15 }) }), onOpen: () => setReading(e), action:
|
|
74
|
+
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), shelf.error && !shelf.value ? (_jsx(Soft, { children: shelf.error.message })) : !shelf.value ? (_jsx(Soft, { children: "Reading the catalogue\u2026" })) : !found.length ? (_jsx(Soft, { children: all.length ? 'No skill matches.' : 'The catalogue lists no skills.' })) : (_jsxs(_Fragment, { children: [_jsx(SizableText, { size: "$1", color: "$soft", children: found.length === all.length ? `${all.length} skills across ${shelf.value.products.length} products` : `${found.length} of ${all.length} skills` }), _jsx(Grid, { label: "Skills to add", children: found.slice(0, shown).map((e) => (_jsx(Tile, { title: e.name, detail: e.description, meta: e.product, mark: _jsx(Mark, { name: e.name, icon: _jsx(BookOpen, { size: 15 }) }), onOpen: () => setReading(e), action: may ? _jsx(Add, { name: e.name, added: lit.has(tool(e.name)), busy: busy === e.name, onPress: () => void flip(e.name, true) }) : undefined }, e.name))) }), found.length > shown ? (_jsx(XStack, { justify: "center", children: _jsx(Button, { size: "sm", variant: "outline", onPress: () => setShown(shown + PAGE), children: "Show more" }) })) : null] })), reader, editor] }));
|
|
73
75
|
}
|
|
74
76
|
if (!signed) {
|
|
75
77
|
return _jsx(Soft, { action: host.signIn ? _jsx(Button, { size: "sm", onPress: () => host.signIn?.(), children: "Sign in" }) : undefined, children: "Sign in to see your skills." });
|
|
@@ -77,9 +79,9 @@ export function Skills({ view, q, adding, onAdding, onView }) {
|
|
|
77
79
|
const ownShown = own.value.filter((s) => matches(q, s.name, s.description));
|
|
78
80
|
const added = on.value.filter((x) => nameOf(x.name) && !mine.has(nameOf(x.name)) && matches(q, nameOf(x.name), x.description));
|
|
79
81
|
const error = own.error ?? on.error;
|
|
80
|
-
return (_jsxs(YStack, { gap: "$5", children: [_jsx(Line, { children: note }), error && !own.value.length && !on.value.length ? (_jsx(Soft, { children: error.message })) : (own.loading || on.loading) && !own.value.length && !on.value.length ? (_jsx(Soft, { children: "Reading your skills\u2026" })) : !own.value.length && !added.length && !q ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover skills" }), children: "No skills yet. Write one, or add one from the catalogue." })) : (_jsxs(_Fragment, { children: [_jsx(Part, { title: "Written here", detail: "Your organization\u2019s own. Off keeps it, and keeps it out of every agent\u2019s prompt.", children: ownShown.length ? (_jsx(Grid, { label: "Your skills", children: ownShown.map((s) => (_jsx(Tile, { title: s.name, detail: s.description || firstLine(s.content), meta: s.source ? `From ${s.source}` : day(s.created) ? `Saved ${day(s.created)}` : undefined, mark: _jsx(Mark, { name: s.name }), onOpen: () => setEditing(s), action: _jsx(Switch, { checked: lit.has(tool(s.name)), disabled: busy === s.name, onCheckedChange: (v) => void flip(s.name, v), "aria-label": `${s.name} on` }) }, s.id))) })) : (_jsx(SizableText, { size: "$2", color: "$soft", children: own.value.length ? 'None of yours matches.' : 'None yet.' })) }), added.length ? (_jsx(Part, { title: "Added from the catalogue", detail: "On for every agent that names them.", children: _jsx(Grid, { label: "Added skills", children: added.map((x) => {
|
|
82
|
+
return (_jsxs(YStack, { gap: "$5", children: [_jsx(Line, { children: note }), error && !own.value.length && !on.value.length ? (_jsx(Soft, { children: error.message })) : (own.loading || on.loading) && !own.value.length && !on.value.length ? (_jsx(Soft, { children: "Reading your skills\u2026" })) : !own.value.length && !added.length && !q ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover skills" }), children: "No skills yet. Write one, or add one from the catalogue." })) : (_jsxs(_Fragment, { children: [_jsx(Part, { title: "Written here", detail: "Your organization\u2019s own. Off keeps it, and keeps it out of every agent\u2019s prompt.", children: ownShown.length ? (_jsx(Grid, { label: "Your skills", children: ownShown.map((s) => (_jsx(Tile, { title: s.name, detail: s.description || firstLine(s.content), meta: s.source ? `From ${s.source}` : day(s.created) ? `Saved ${day(s.created)}` : undefined, mark: _jsx(Mark, { name: s.name }), onOpen: () => setEditing(s), action: may ? (_jsx(Switch, { checked: lit.has(tool(s.name)), disabled: busy === s.name, onCheckedChange: (v) => void flip(s.name, v), "aria-label": `${s.name} on` })) : (_jsx(SizableText, { size: "$1", color: "$soft", children: lit.has(tool(s.name)) ? 'On' : 'Off' })) }, s.id))) })) : (_jsx(SizableText, { size: "$2", color: "$soft", children: own.value.length ? 'None of yours matches.' : 'None yet.' })) }), added.length ? (_jsx(Part, { title: "Added from the catalogue", detail: "On for every agent that names them.", children: _jsx(Grid, { label: "Added skills", children: added.map((x) => {
|
|
81
83
|
const name = nameOf(x.name);
|
|
82
|
-
return (_jsx(Tile, { title: name, detail: x.description, mark: _jsx(Mark, { name: name, icon: _jsx(BookOpen, { size: 15 }) }), onOpen: () => setReading({ name, description: x.description, product: '' }), action: _jsx(Button, { size: "sm", variant: "ghost", disabled: busy === name, onPress: () => void flip(name, false), "aria-label": `Remove ${name}`, children: "Remove" }) }, x.name));
|
|
84
|
+
return (_jsx(Tile, { title: name, detail: x.description, mark: _jsx(Mark, { name: name, icon: _jsx(BookOpen, { size: 15 }) }), onOpen: () => setReading({ name, description: x.description, product: '' }), action: may ? (_jsx(Button, { size: "sm", variant: "ghost", disabled: busy === name, onPress: () => void flip(name, false), "aria-label": `Remove ${name}`, children: "Remove" })) : undefined }, x.name));
|
|
83
85
|
}) }) })) : null] })), reader, editor] }));
|
|
84
86
|
}
|
|
85
87
|
const firstLine = (md) => md
|
|
@@ -93,7 +95,7 @@ function Reader({ skill, added, signed, busy, onFlip, onClose, }) {
|
|
|
93
95
|
return (_jsxs(Sheet, { title: skill.name, open: true, onOpenChange: (o) => !o && onClose(), width: 720, children: [skill.description ? (_jsx(SizableText, { size: "$2", color: "$soft", children: skill.description })) : null, signed ? (_jsxs(XStack, { gap: "$2", items: "center", children: [_jsx(Button, { size: "sm", variant: added ? 'outline' : 'default', disabled: busy, onPress: () => onFlip(!added), children: added ? 'Remove' : 'Add to your skills' }), _jsx(SizableText, { size: "$1", color: "$soft", flex: 1, children: added ? 'On for your organization.' : 'Adding it switches it on for your organization.' })] })) : null, doc.error ? (_jsx(Soft, { children: doc.error.message })) : doc.loading && !doc.value ? (_jsx(Soft, { children: "Reading SKILL.md\u2026" })) : (_jsx(Code, { language: "SKILL.md", value: doc.value, children: _jsx(SizableText, { size: "$1", color: "$ink", style: { ...mono, whiteSpace: 'pre-wrap' }, children: doc.value }) }))] }));
|
|
94
96
|
}
|
|
95
97
|
/** Write a skill, or revise one of the org's own. Its name is its id, so a saved skill keeps its name. */
|
|
96
|
-
function Editor({ skill, onClose, onSaved, onDeleted, }) {
|
|
98
|
+
function Editor({ skill, may, onClose, onSaved, onDeleted, }) {
|
|
97
99
|
const t = useTarget();
|
|
98
100
|
const [name, setName] = useState(skill?.name ?? '');
|
|
99
101
|
const [description, setDescription] = useState(skill?.description ?? '');
|
|
@@ -118,7 +120,7 @@ function Editor({ skill, onClose, onSaved, onDeleted, }) {
|
|
|
118
120
|
setWorking(false);
|
|
119
121
|
}
|
|
120
122
|
};
|
|
121
|
-
return (_jsxs(Sheet, { title: skill ? skill.name : 'New skill', open: true, onOpenChange: (o) => !o && onClose(), width: 680, children: [skill?.source ? (_jsxs(SizableText, { size: "$2", color: "$soft", children: ["Read from ", skill.source, ". The next push of that repository replaces what is saved here."] })) : null, _jsx(Field, { label: "Name", hint: skill ? 'A skill’s name is its id; save under another name to make a new one.' : 'One lowercase word: letters, digits, _ or -. Agents name it as skill_<name>.', children: _jsx(Input, { value: name, onChangeText: setName, placeholder: "triage", "aria-label": "Name", disabled: Boolean(skill), autoCapitalize: "none" }) }), _jsx(Field, { label: "Description", hint: "The one line an agent reads to decide whether it needs this skill.", children: _jsx(Input, { value: description, onChangeText: setDescription, placeholder: "How we triage an incoming issue", "aria-label": "Description" }) }), _jsx(Field, { label: "SKILL.md", hint: "Markdown: what the skill is for, when to use it, and the steps.", children: _jsx(Textarea, { value: content, onChangeText: setContent, placeholder: '# Triage\n\n1. Read the issue…', "aria-label": "SKILL.md", rows: 14, style: mono }) }), _jsx(Line, { children: note }), _jsxs(XStack, { gap: "$2", items: "center", children: [skill ? (_jsx(Button, { size: "sm", variant: "ghost", onPress: () => setAsking(true), children: "Delete" })) : null, _jsx(XStack, { flex: 1 }), _jsx(Button, { size: "sm", variant: "ghost", onPress: onClose, children:
|
|
123
|
+
return (_jsxs(Sheet, { title: skill ? skill.name : 'New skill', open: true, onOpenChange: (o) => !o && onClose(), width: 680, children: [skill?.source ? (_jsxs(SizableText, { size: "$2", color: "$soft", children: ["Read from ", skill.source, ". The next push of that repository replaces what is saved here."] })) : null, _jsx(Field, { label: "Name", hint: skill ? 'A skill’s name is its id; save under another name to make a new one.' : 'One lowercase word: letters, digits, _ or -. Agents name it as skill_<name>.', children: _jsx(Input, { value: name, onChangeText: setName, placeholder: "triage", "aria-label": "Name", disabled: Boolean(skill), autoCapitalize: "none" }) }), _jsx(Field, { label: "Description", hint: "The one line an agent reads to decide whether it needs this skill.", children: _jsx(Input, { value: description, onChangeText: setDescription, placeholder: "How we triage an incoming issue", "aria-label": "Description" }) }), _jsx(Field, { label: "SKILL.md", hint: "Markdown: what the skill is for, when to use it, and the steps.", children: _jsx(Textarea, { value: content, onChangeText: setContent, placeholder: '# Triage\n\n1. Read the issue…', "aria-label": "SKILL.md", rows: 14, style: mono }) }), _jsx(Line, { children: note || (may ? '' : 'An org admin writes and deletes the organization’s skills.') }), _jsxs(XStack, { gap: "$2", items: "center", children: [skill && may ? (_jsx(Button, { size: "sm", variant: "ghost", onPress: () => setAsking(true), children: "Delete" })) : null, _jsx(XStack, { flex: 1 }), _jsx(Button, { size: "sm", variant: "ghost", onPress: onClose, children: may ? 'Cancel' : 'Close' }), may ? (_jsx(Button, { size: "sm", disabled: working, onPress: () => void save(), children: "Save" })) : null] }), skill ? (_jsx(Confirm, { what: skill.name, says: "The skill leaves your organization and every agent that names it.", open: asking, onOpenChange: setAsking, onYes: async () => {
|
|
122
124
|
await remove(t, skill.id);
|
|
123
125
|
onDeleted(skill.name);
|
|
124
126
|
} })) : null] }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"description": "Hanzo Build — say what you want, watch it built, deployed and running. The builder as a page (hanzo.build) and as a component (hanzo.ai/dev).",
|