Skip to content

Commit 7c35466

Browse files
committed
feat: add skills list panel and related commands
- Introduced a new SkillsListPanel component for managing skills. - Updated usePanelState hook to include showSkillsListPanel state. - Enhanced i18n support for skills-related messages in English, Traditional Chinese, and Simplified Chinese. - Implemented /loop command with localized messages for better user experience. - Modified /skills command to toggle the skills list panel. - Updated command execution types to include showSkillsListPanel action. - Improved SessionListPanel to confirm deletion of sessions with a timer. - Cleaned up MCPInfoPanel by removing unused skill-related code.
1 parent d217003 commit 7c35466

16 files changed

Lines changed: 572 additions & 230 deletions

File tree

source/hooks/conversation/useCommandHandler.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ type CommandHandlerOptions = {
417417
setShowSubAgentDepthPanel: React.Dispatch<React.SetStateAction<boolean>>;
418418
setShowCustomCommandConfig: React.Dispatch<React.SetStateAction<boolean>>;
419419
setShowSkillsCreation: React.Dispatch<React.SetStateAction<boolean>>;
420+
setShowSkillsListPanel: React.Dispatch<React.SetStateAction<boolean>>;
420421
setShowRoleCreation: React.Dispatch<React.SetStateAction<boolean>>;
421422
setShowRoleDeletion: React.Dispatch<React.SetStateAction<boolean>>;
422423
setShowRoleList: React.Dispatch<React.SetStateAction<boolean>>;
@@ -737,6 +738,14 @@ export function useCommandHandler(options: CommandHandlerOptions) {
737738
commandName: commandName,
738739
};
739740
options.setMessages(prev => [...prev, commandMessage]);
741+
} else if (result.success && result.action === 'showSkillsListPanel') {
742+
options.setShowSkillsListPanel(true);
743+
const commandMessage: Message = {
744+
role: 'command',
745+
content: '',
746+
commandName: commandName,
747+
};
748+
options.setMessages(prev => [...prev, commandMessage]);
740749
} else if (result.success && result.action === 'showRoleCreation') {
741750
options.setShowRoleCreation(true);
742751
const commandMessage: Message = {

source/hooks/ui/useCommandPanel.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const COMMAND_ARGS_HINTS: Record<string, string> = {
3737
'add-dir': '[path]',
3838
loop: '<interval> <prompt> | list | tasks | cancel <id>',
3939
role: '[-l|--list | -d|--delete]',
40+
skills: '[-l|--list]',
4041
'role-subagent': '[-l|--list | -d|--delete]',
4142
'subagent-depth': '[<number>|status]',
4243
btw: '<question>',
@@ -51,9 +52,10 @@ export const COMMAND_ARGS_OPTIONS: Record<string, string[]> = {
5152
'auto-format': ['on', 'off', 'status'],
5253
reindex: ['-force'],
5354
role: ['-l', '-d'],
55+
skills: ['-l'],
5456
'role-subagent': ['-l', '-d'],
5557
'subagent-depth': ['status'],
56-
loop: ['list', 'tasks'],
58+
loop: ['list', 'tasks', 'cancel'],
5759
};
5860

5961
export function useCommandPanel(buffer: TextBuffer, isProcessing = false) {

source/hooks/ui/usePanelState.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type PanelState = {
1313
showHelpPanel: boolean;
1414
showCustomCommandConfig: boolean;
1515
showSkillsCreation: boolean;
16+
showSkillsListPanel: boolean;
1617
showRoleCreation: boolean;
1718
showRoleDeletion: boolean;
1819
showRoleList: boolean;
@@ -49,6 +50,7 @@ export type PanelActions = {
4950
setConnectionPanelApiUrl: Dispatch<SetStateAction<string | undefined>>;
5051
setShowCustomCommandConfig: Dispatch<SetStateAction<boolean>>;
5152
setShowSkillsCreation: Dispatch<SetStateAction<boolean>>;
53+
setShowSkillsListPanel: Dispatch<SetStateAction<boolean>>;
5254
setShowRoleCreation: Dispatch<SetStateAction<boolean>>;
5355
setShowRoleDeletion: Dispatch<SetStateAction<boolean>>;
5456
setShowRoleList: Dispatch<SetStateAction<boolean>>;
@@ -95,6 +97,7 @@ export function usePanelState(): PanelState & PanelActions {
9597
const [showHelpPanel, setShowHelpPanel] = useState(false);
9698
const [showCustomCommandConfig, setShowCustomCommandConfig] = useState(false);
9799
const [showSkillsCreation, setShowSkillsCreation] = useState(false);
100+
const [showSkillsListPanel, setShowSkillsListPanel] = useState(false);
98101
const [showRoleCreation, setShowRoleCreation] = useState(false);
99102
const [showRoleDeletion, setShowRoleDeletion] = useState(false);
100103
const [showRoleList, setShowRoleList] = useState(false);
@@ -143,6 +146,7 @@ export function usePanelState(): PanelState & PanelActions {
143146
showUsagePanel ||
144147
showCustomCommandConfig ||
145148
showSkillsCreation ||
149+
showSkillsListPanel ||
146150
showRoleCreation ||
147151
showRoleDeletion ||
148152
showRoleList ||
@@ -246,6 +250,11 @@ export function usePanelState(): PanelState & PanelActions {
246250
if (showSkillsCreation) {
247251
return false; // Let SkillsCreationPanel handle ESC
248252
}
253+
if (showSkillsListPanel) {
254+
setShowSkillsListPanel(false);
255+
return true;
256+
}
257+
249258
// RoleCreationPanel handles its own ESC key logic internally
250259
// Don't close it here - let the panel decide when to close
251260
if (showRoleCreation) {
@@ -349,6 +358,7 @@ export function usePanelState(): PanelState & PanelActions {
349358
showUsagePanel ||
350359
showCustomCommandConfig ||
351360
showSkillsCreation ||
361+
showSkillsListPanel ||
352362
showRoleCreation ||
353363
showRoleDeletion ||
354364
showRoleList ||
@@ -378,6 +388,7 @@ export function usePanelState(): PanelState & PanelActions {
378388
showHelpPanel,
379389
showCustomCommandConfig,
380390
showSkillsCreation,
391+
showSkillsListPanel,
381392
showRoleCreation,
382393
showRoleDeletion,
383394
showRoleList,
@@ -408,6 +419,7 @@ export function usePanelState(): PanelState & PanelActions {
408419
setShowHelpPanel,
409420
setShowCustomCommandConfig,
410421
setShowSkillsCreation,
422+
setShowSkillsListPanel,
411423
setShowRoleCreation,
412424
setShowRoleDeletion,
413425
setShowRoleList,

source/i18n/lang/en.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,25 @@ export const en: TranslationKeys = {
703703
usage:
704704
'Usage: /deepresearch <prompt>\nExample: /deepresearch Compare the architectures of OpenAI Deep Research and Gemini Deep Research',
705705
},
706+
// Loop command messages
707+
loop: {
708+
usage:
709+
'Usage: /loop 5m <prompt> | /loop 8h30m <prompt> | /loop <prompt> every 2 hours | /loop list | /loop cancel <id> | /loop tasks',
710+
openingTaskManager: 'Opening task manager...',
711+
relatedLoopTasks: 'Related loop tasks:',
712+
noActiveLoops:
713+
'No active loops. Create one with /loop 5m <prompt> or /loop <prompt> every 2 hours.',
714+
loopNotFound: 'Loop not found: {id}',
715+
cancelled: 'Cancelled loop {id} (every {interval})',
716+
created: 'Loop created: {id}',
717+
scheduleEvery: 'Schedule: every {interval}',
718+
promptLabel: 'Prompt: {prompt}',
719+
nextRun: 'Next run: {time}',
720+
sessionScopedNote:
721+
'Session-scoped only: loop jobs stop when Snow CLI exits.',
722+
usageHint:
723+
'Use /loop list to inspect jobs or /loop cancel <id> to stop one.',
724+
},
706725
},
707726
},
708727
fileList: {
@@ -1624,6 +1643,7 @@ export const en: TranslationKeys = {
16241643
renamePrompt: 'Rename Session',
16251644
renaming: 'Renaming...',
16261645
renamePlaceholder: 'Enter new title',
1646+
confirmDelete: 'Press D again within 1s to confirm delete ({count})',
16271647
},
16281648
mcpInfoPanel: {
16291649
title: 'MCP Services',
@@ -1657,6 +1677,18 @@ export const en: TranslationKeys = {
16571677
mcpSourceProject: ' [Project]',
16581678
mcpSourceGlobal: ' [Global]',
16591679
},
1680+
skillsListPanel: {
1681+
title: 'Skills',
1682+
loading: 'Loading skills...',
1683+
error: 'Error: {message}',
1684+
noSkills: 'No skills available',
1685+
locationProject: '(Project)',
1686+
locationGlobal: '(Global)',
1687+
statusDisabled: '(Disabled)',
1688+
navigationHint: '↑↓ Navigate • Tab/Space/Enter Toggle • ESC Close',
1689+
moreAbove: '↑ {count} more above',
1690+
moreBelow: '↓ {count} more below',
1691+
},
16601692
mcpConfigScreen: {
16611693
title: 'MCP Config - Select scope to edit',
16621694
scopeProject: 'Project Config',

source/i18n/lang/zh-TW.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,24 @@ export const zhTW: TranslationKeys = {
661661
usage:
662662
'用法: /deepresearch <提示詞>\n範例: /deepresearch 對比 OpenAI Deep Research 與 Gemini Deep Research 的架構差異',
663663
},
664+
// Loop 命令訊息
665+
loop: {
666+
usage:
667+
'用法: /loop 5m <提示詞> | /loop 8h30m <提示詞> | /loop <提示詞> every 2 hours | /loop list | /loop cancel <id> | /loop tasks',
668+
openingTaskManager: '正在開啟任務管理員...',
669+
relatedLoopTasks: '相關迴圈任務:',
670+
noActiveLoops:
671+
'目前沒有活躍的迴圈任務。可使用 /loop 5m <提示詞> 或 /loop <提示詞> every 2 hours 建立。',
672+
loopNotFound: '找不到迴圈任務: {id}',
673+
cancelled: '已取消迴圈任務 {id}(每 {interval})',
674+
created: '迴圈任務已建立: {id}',
675+
scheduleEvery: '排程: 每 {interval}',
676+
promptLabel: '提示詞: {prompt}',
677+
nextRun: '下次執行: {time}',
678+
sessionScopedNote: '僅限會話作用域: Snow CLI 結束後迴圈任務將停止。',
679+
usageHint:
680+
'使用 /loop list 檢視任務,或使用 /loop cancel <id> 停止某個任務。',
681+
},
664682
},
665683
},
666684
fileList: {
@@ -1537,6 +1555,7 @@ export const zhTW: TranslationKeys = {
15371555
renamePrompt: '重新命名會話',
15381556
renaming: '重新命名中...',
15391557
renamePlaceholder: '輸入新的標題',
1558+
confirmDelete: '1 秒內再按一次 D 確認刪除(共 {count} 個)',
15401559
},
15411560
mcpInfoPanel: {
15421561
title: 'MCP 服務',
@@ -1568,6 +1587,18 @@ export const zhTW: TranslationKeys = {
15681587
mcpSourceProject: ' [專案]',
15691588
mcpSourceGlobal: ' [全域]',
15701589
},
1590+
skillsListPanel: {
1591+
title: '技能列表',
1592+
loading: '載入技能中...',
1593+
error: '錯誤: {message}',
1594+
noSkills: '沒有可用的技能',
1595+
locationProject: '(專案)',
1596+
locationGlobal: '(全域)',
1597+
statusDisabled: '(已停用)',
1598+
navigationHint: '↑↓ 導航 • Tab/空格/Enter 啟停 • ESC 關閉',
1599+
moreAbove: '↑ 上方還有 {count} 項',
1600+
moreBelow: '↓ 下方還有 {count} 項',
1601+
},
15711602
mcpConfigScreen: {
15721603
title: 'MCP 設定 - 選擇編輯範圍',
15731604
scopeProject: '專案級設定',

source/i18n/lang/zh.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,24 @@ export const zh: TranslationKeys = {
660660
usage:
661661
'用法: /deepresearch <提示词>\n示例: /deepresearch 对比 OpenAI Deep Research 与 Gemini Deep Research 的架构差异',
662662
},
663+
// Loop 命令消息
664+
loop: {
665+
usage:
666+
'用法: /loop 5m <提示词> | /loop 8h30m <提示词> | /loop <提示词> every 2 hours | /loop list | /loop cancel <id> | /loop tasks',
667+
openingTaskManager: '正在打开任务管理器...',
668+
relatedLoopTasks: '相关循环任务:',
669+
noActiveLoops:
670+
'暂无活跃的循环任务。可使用 /loop 5m <提示词> 或 /loop <提示词> every 2 hours 创建。',
671+
loopNotFound: '未找到循环任务: {id}',
672+
cancelled: '已取消循环任务 {id}(每 {interval})',
673+
created: '循环任务已创建: {id}',
674+
scheduleEvery: '调度: 每 {interval}',
675+
promptLabel: '提示词: {prompt}',
676+
nextRun: '下次运行: {time}',
677+
sessionScopedNote: '仅限会话作用域: Snow CLI 退出后循环任务将停止。',
678+
usageHint:
679+
'使用 /loop list 查看任务,或使用 /loop cancel <id> 停止某个任务。',
680+
},
663681
},
664682
},
665683
fileList: {
@@ -1541,6 +1559,7 @@ export const zh: TranslationKeys = {
15411559
renamePrompt: '重命名会话',
15421560
renaming: '重命名中...',
15431561
renamePlaceholder: '输入新的标题',
1562+
confirmDelete: '1 秒内再按一次 D 确认删除(共 {count} 个)',
15441563
},
15451564
mcpInfoPanel: {
15461565
title: 'MCP 服务',
@@ -1572,6 +1591,18 @@ export const zh: TranslationKeys = {
15721591
mcpSourceProject: ' [项目]',
15731592
mcpSourceGlobal: ' [全局]',
15741593
},
1594+
skillsListPanel: {
1595+
title: '技能列表',
1596+
loading: '加载技能中...',
1597+
error: '错误: {message}',
1598+
noSkills: '没有可用的技能',
1599+
locationProject: '(项目)',
1600+
locationGlobal: '(全局)',
1601+
statusDisabled: '(已禁用)',
1602+
navigationHint: '↑↓ 导航 • Tab/空格/Enter 启停 • ESC 关闭',
1603+
moreAbove: '↑ 上方还有 {count} 项',
1604+
moreBelow: '↓ 下方还有 {count} 项',
1605+
},
15751606
mcpConfigScreen: {
15761607
title: 'MCP 配置 - 选择编辑范围',
15771608
scopeProject: '项目级配置',

source/i18n/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,21 @@ export type TranslationKeys = {
646646
deepResearch: {
647647
usage: string;
648648
};
649+
// Loop command messages
650+
loop: {
651+
usage: string;
652+
openingTaskManager: string;
653+
relatedLoopTasks: string;
654+
noActiveLoops: string;
655+
loopNotFound: string;
656+
cancelled: string;
657+
created: string;
658+
scheduleEvery: string;
659+
promptLabel: string;
660+
nextRun: string;
661+
sessionScopedNote: string;
662+
usageHint: string;
663+
};
649664
};
650665
};
651666
// File search list (`@` panel)
@@ -1521,6 +1536,7 @@ export type TranslationKeys = {
15211536
renamePrompt: string;
15221537
renaming: string;
15231538
renamePlaceholder: string;
1539+
confirmDelete: string;
15241540
};
15251541
mcpInfoPanel: {
15261542
title: string;
@@ -1552,6 +1568,18 @@ export type TranslationKeys = {
15521568
mcpSourceProject: string;
15531569
mcpSourceGlobal: string;
15541570
};
1571+
skillsListPanel: {
1572+
title: string;
1573+
loading: string;
1574+
error: string;
1575+
noSkills: string;
1576+
locationProject: string;
1577+
locationGlobal: string;
1578+
statusDisabled: string;
1579+
navigationHint: string;
1580+
moreAbove: string;
1581+
moreBelow: string;
1582+
};
15551583
mcpConfigScreen: {
15561584
title: string;
15571585
scopeProject: string;

source/ui/components/chat/ChatFooter.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {ReviewCommitSelection} from '../panels/ReviewCommitPanel.js';
1919
import {IdeSelectPanel} from '../panels/IdeSelectPanel.js';
2020
const BtwPanel = lazy(() => import('../panels/BtwPanel.js'));
2121
const DiffReviewPanel = lazy(() => import('../panels/DiffReviewPanel.js'));
22+
const SkillsListPanel = lazy(() => import('../panels/SkillsListPanel.js'));
2223

2324
type ChatFooterProps = {
2425
onSubmit: (
@@ -147,6 +148,10 @@ type ChatFooterProps = {
147148
message?: string,
148149
) => void;
149150

151+
// Skills list panel props
152+
showSkillsListPanel: boolean;
153+
setShowSkillsListPanel: React.Dispatch<React.SetStateAction<boolean>>;
154+
150155
// BTW panel props
151156
btwPrompt: string | null;
152157
onBtwClose: () => void;
@@ -270,17 +275,19 @@ const ChatFooter = React.memo(function ChatFooter(props: ChatFooterProps) {
270275
if (
271276
props.showReviewCommitPanel ||
272277
props.showIdeSelectPanel ||
273-
props.showDiffReviewPanel
278+
props.showDiffReviewPanel ||
279+
props.showSkillsListPanel
274280
) {
275281
props.onDraftChange(null);
276282
}
277-
}, [props.showReviewCommitPanel, props.showIdeSelectPanel, props.showDiffReviewPanel]);
283+
}, [props.showReviewCommitPanel, props.showIdeSelectPanel, props.showDiffReviewPanel, props.showSkillsListPanel]);
278284

279285
return (
280286
<>
281287
{!props.showReviewCommitPanel &&
282288
!props.showIdeSelectPanel &&
283-
!props.showDiffReviewPanel && (
289+
!props.showDiffReviewPanel &&
290+
!props.showSkillsListPanel && (
284291
<>
285292
<LoadingIndicator
286293
isStreaming={props.isStreaming}
@@ -428,6 +435,24 @@ const ChatFooter = React.memo(function ChatFooter(props: ChatFooterProps) {
428435
/>
429436
)}
430437

438+
{props.showSkillsListPanel && (
439+
<Box marginTop={1} flexDirection="column">
440+
<Suspense
441+
fallback={
442+
<Box>
443+
<Text>
444+
<Spinner type="dots" /> Loading...
445+
</Text>
446+
</Box>
447+
}
448+
>
449+
<SkillsListPanel
450+
onClose={() => props.setShowSkillsListPanel(false)}
451+
/>
452+
</Suspense>
453+
</Box>
454+
)}
455+
431456
{props.showDiffReviewPanel && (
432457
<Suspense
433458
fallback={

0 commit comments

Comments
 (0)