feat(skills): surface Company Brain-created skills (#1412)

## What and why

Make skills created through Company Brain visible in the existing Settings catalog without a manual reload. The UI labels Slack-originated skills and refreshes the catalog while the Settings page is open.

```mermaid
flowchart LR
  A[Slack approval] --> B[Company Brain skill catalog]
  B --> C[Skills API query]
  C --> D[Settings Skills list]
  D --> E[Created by Company Brain label]
```

## Validation

- `bunx biome check apps/web/components/settings/company-brain-skills.tsx apps/web/components/settings/company-brain-skills/domain.ts apps/web/components/settings/company-brain-skills/skill-row.tsx apps/web/hooks/use-brain-skills.impl.ts` — passed

## Impact

Settings polling refreshes every 15 seconds and on window focus. It does not change the Nova/browser chat workflow or create skills from that surface.
This commit is contained in:
sreedharsreeram 2026-08-05 03:42:37 +00:00
parent 4d86b728dd
commit 570ed22b6c
4 changed files with 24 additions and 1 deletions

View file

@ -335,7 +335,8 @@ function CompanyBrainSkillsContent({
</div>
<p className="text-[11px] leading-5 text-[#596270]">
{SCOPE_HINTS[scopeFilter]}
{SCOPE_HINTS[scopeFilter]} Skills created through Company Brain
appear here automatically.
</p>
{uploadError ? (

View file

@ -13,6 +13,7 @@ export type SkillDraft = {
export type SkillMarkdown = Pick<SkillDraft, "name" | "description" | "body">
export type NewSkillOrigin = "web" | "upload"
export type SkillOrigin = NewSkillOrigin | "slack"
export type SkillAuthoringTarget = {
canEdit: boolean
@ -51,6 +52,10 @@ export function emptySkillDraft(): SkillDraft {
}
}
export function skillOriginLabel(origin: SkillOrigin): string | null {
return origin === "slack" ? "Created by Company Brain" : null
}
export function setSkillDraftScope(
draft: SkillDraft,
scope: SkillScope,

View file

@ -3,6 +3,7 @@
import { cn } from "@lib/utils"
import { formatDistanceToNow } from "date-fns"
import { ChevronRight } from "lucide-react"
import { skillOriginLabel } from "@/components/settings/company-brain-skills/domain"
import type { BrainSkill } from "@/hooks/use-brain-skills"
import { dmSans125ClassName } from "@/lib/fonts"
@ -27,6 +28,16 @@ function ScopeBadge({ scope }: { scope: BrainSkill["scope"] }) {
)
}
function OriginBadge({ origin }: { origin: BrainSkill["origin"] }) {
const label = skillOriginLabel(origin)
if (!label) return null
return (
<span className="inline-flex h-[18px] shrink-0 items-center rounded-full bg-[#1F2E3C] px-1.5 text-[10px] font-medium text-[#A8C7E8]">
{label}
</span>
)
}
export function SkillRow({
skill,
onOpen,
@ -51,6 +62,7 @@ export function SkillRow({
{skill.name}
</h3>
<ScopeBadge scope={skill.scope} />
<OriginBadge origin={skill.origin} />
{skill.status === "disabled" ? (
<span className="inline-flex h-[18px] shrink-0 items-center rounded-full border border-red-400/20 bg-red-400/[0.07] px-1.5 text-[10px] font-medium text-red-300">
Disabled

View file

@ -5,6 +5,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import type {
NewSkillOrigin,
SkillDraft,
SkillOrigin,
SkillScope,
} from "@/components/settings/company-brain-skills/domain"
import { skillSaveRequestBody } from "@/components/settings/company-brain-skills/domain"
@ -23,6 +24,7 @@ export type BrainSkill = {
scope: SkillScope
status: BrainSkillStatus
creatorUserId: string
origin: SkillOrigin
canEdit: boolean
canDelete: boolean
version: number
@ -86,6 +88,9 @@ export function useBrainSkills() {
queryFn: () =>
jsonRequest<BrainSkillsResponse>(`${BASE}/`, {}, "Couldn't load skills."),
enabled: !!org?.id && !!user?.id,
staleTime: 0,
refetchOnWindowFocus: true,
refetchInterval: 15_000,
})
}