Skip to content

Commit 8a768b6

Browse files
authored
feat(ui): Skills management surface — unhide and rebuild the Skills tab (ent#235) (#1877)
* feat(ui): Skills management surface — unhide and rebuild the Skills tab (ent#235) The skills machinery shipped across three planes (#182 distribute/place/expose, #183 package injection with a per-skill result contract) and nothing rendered any of it. The Agent Detail Skills tab was excluded from `visibleTabs` per requirements §22.2 ("component preserved for potential admin-only access"), assignment was REST/MCP-only (§21.3), and #183's statuses and named warnings had no consumer at all. A user could not browse the library, see what an agent had, or assign anything. What lands: * **Tab unhidden** for owners/admins on non-system agents, matching the other management tabs. `OverflowTabs` absorbs it. * **`stores/skills.js`** — a domain store (Invariant #6). The old panel called `axios` directly with a hand-built auth header, silently bypassing the shared client every other call relies on; everything now goes through `api` (Invariant #7). * **Library browse** with the §21.6 contract surfaced: description, automation, `user_invocable`, declared `requires` (binaries/packages/env), multi-file file count, size, and the git tree SHA as version. Dependencies are shown BEFORE assignment, because they are what later becomes a `missing_binary:*` warning. * **Assignment** with bulk save through the existing `PUT .../skills`, plus a dirty/reset affordance so a half-made selection is recoverable. * **Honest injection status.** This is the load-bearing part. #183 reports `injected | unchanged | fallback | failed` with named warnings; the panel renders the verdict per skill and translates the tokens into what they mean for THIS agent ("`jq` is not installed in this agent — the skill may not run"). `fallback` renders as "partial" in amber, never a green tick — an explicit AC. Injection results are kept separate from assignment in the store precisely so a durable assignment cannot be painted with a stale success. * **Manual sync** (`force=True` repair action) with in-flight state, and a 409 from `SkillInjectionBusy` reported as "already running" rather than a generic failure. * **No dead empty states** — the store computes one discriminator (`library_unconfigured` / `library_empty` / `none_assigned`) so the panel cannot invent a fourth. Unconfigured routes an admin to Settings and tells a non-admin to ask one. * **Stopped agent** renders persisted assignment state with Sync disabled and the reason in the tooltip, rather than offering an action that would fail. Verified against the live instance: tab appears, 3-skill library renders with contract fields, bulk assign persists, agent started, "Sync now" returns `{haiku: injected/2 files, word-count: injected/2 files}` and the badges + last-sync line render from that response. Gating confirmed OSS-core with the issue author before building: every file here is already public, the endpoints are ungated, and the paid piece (skill_runner, ent#139) plus exposure curation (#178) are both explicitly out of scope. Related to trinity-enterprise#235 * fix(ui): dead Settings link + error swallowed as an empty library (ent#235 review) Self-review of #1877 found two defects, both in the "no dead empty states" AC this panel exists to satisfy. 1) The "Configure the library" CTA linked to `/settings?tab=skills`. There is no such tab — the Skills Library config lives under Settings → **agents** (`Settings.vue`, `v-if="activeTab === 'agents'"`). So the one call-to-action offered to an admin staring at an unconfigured library went nowhere. I also asserted in the PR body that the Settings panel already reported sync status / last-synced / skill count without checking; it does report all three — but I had the tab wrong, which is what checking would have caught. 2) `api.get('/api/skills/library').catch(() => ({ data: [] }))` swallowed every error, not just the unconfigured case: a 500, a timeout or an auth failure all rendered as "the library is configured but has no skills yet" — a confident, wrong empty state that points the operator at the wrong problem. The list is now fetched only when `status.configured` is true, so the known empty state comes from the status read and any other failure surfaces as one. Related to trinity-enterprise#235
1 parent dc4f490 commit 8a768b6

3 files changed

Lines changed: 396 additions & 282 deletions

File tree

0 commit comments

Comments
 (0)