From 949b8f5521a6fc98d472f58aa9be3dedaa90e1d3 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sat, 18 Apr 2026 09:29:39 -0500 Subject: [PATCH] feat(tui): register /skills slash command to open Skills Hub Intercept bare /skills locally and flip overlay.skillsHub, so the overlay opens instantly without waiting on slash.exec. /skills still forwards to slash.exec and paginates any output. Tests cover both branches. --- .../src/__tests__/createSlashHandler.test.ts | 20 ++++++++++++++++ ui-tui/src/app/slash/commands/ops.ts | 24 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/ui-tui/src/__tests__/createSlashHandler.test.ts b/ui-tui/src/__tests__/createSlashHandler.test.ts index 9e1db99463..c54a659b94 100644 --- a/ui-tui/src/__tests__/createSlashHandler.test.ts +++ b/ui-tui/src/__tests__/createSlashHandler.test.ts @@ -17,6 +17,26 @@ describe('createSlashHandler', () => { expect(getOverlayState().picker).toBe(true) }) + it('opens the skills hub locally for bare /skills', () => { + const ctx = buildCtx() + + expect(createSlashHandler(ctx)('/skills')).toBe(true) + expect(getOverlayState().skillsHub).toBe(true) + expect(ctx.gateway.rpc).not.toHaveBeenCalled() + expect(ctx.gateway.gw.request).not.toHaveBeenCalled() + }) + + it('falls through /skills with args to slash.exec without opening overlay', () => { + const ctx = buildCtx() + + expect(createSlashHandler(ctx)('/skills install foo')).toBe(true) + expect(getOverlayState().skillsHub).toBe(false) + expect(ctx.gateway.rpc).toHaveBeenCalledWith('slash.exec', { + command: 'skills install foo', + session_id: null + }) + }) + it('cycles details mode and persists it', async () => { const ctx = buildCtx() diff --git a/ui-tui/src/app/slash/commands/ops.ts b/ui-tui/src/app/slash/commands/ops.ts index 979e1f470a..aa02fa6cbb 100644 --- a/ui-tui/src/app/slash/commands/ops.ts +++ b/ui-tui/src/app/slash/commands/ops.ts @@ -1,7 +1,29 @@ -import type { ToolsConfigureResponse } from '../../../gatewayTypes.js' +import type { SlashExecResponse, ToolsConfigureResponse } from '../../../gatewayTypes.js' +import { patchOverlayState } from '../../overlayStore.js' import type { SlashCommand } from '../types.js' export const opsCommands: SlashCommand[] = [ + { + help: 'browse, inspect, and install skills', + name: 'skills', + run: (arg, ctx) => { + if (!arg.trim()) { + return patchOverlayState({ skillsHub: true }) + } + + ctx.gateway + .rpc('slash.exec', { command: `skills ${arg}`, session_id: ctx.sid }) + .then( + ctx.guarded(r => { + if (r.output) { + ctx.transcript.page(r.output, 'Skills') + } + }) + ) + .catch(ctx.guardedErr) + } + }, + { help: 'enable or disable tools (client-side history reset on change)', name: 'tools',