@@ -44,15 +44,27 @@ function formatProjectLines(projects, currentWorkdir) {
4444 } ) ;
4545}
4646
47- export function registerHandlers ( { bot, router, ptyManager, shellManager, skills, scheduler } ) {
47+ function formatSkillLines ( skillStates ) {
48+ return skillStates . map ( ( skill ) => `- ${ skill . name } : ${ skill . enabled ? "on" : "off" } ` ) ;
49+ }
50+
51+ export function registerHandlers ( {
52+ bot,
53+ router,
54+ ptyManager,
55+ shellManager,
56+ skills,
57+ skillRegistry,
58+ scheduler
59+ } ) {
4860 bot . start ( async ( ctx ) => {
4961 await sendChunkedMarkdown (
5062 ctx ,
5163 [
5264 "codex-telegram-claws ready." ,
5365 "普通消息和编码任务会路由到 Codex。" ,
5466 "MCP 只在显式 /mcp 命令下调用。" ,
55- "试试: /status, /repo, /pwd, /exec, /auto, /plan, /model, /new, /sh" ,
67+ "试试: /status, /repo, /pwd, /exec, /auto, /plan, /model, /skill, / new, /sh" ,
5668 "GitHub 指令示例: /gh commit \"feat: init\""
5769 ] . join ( "\n" )
5870 ) ;
@@ -76,19 +88,24 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
7688 "/auto <task> - 强制用 codex exec --full-auto 运行任务" ,
7789 "/plan <task> - 仅生成执行计划,不直接修改代码" ,
7890 "/model [name|reset] - 查看或设置当前 chat 的模型" ,
91+ "/skill list - 查看当前 chat 的 skill 开关" ,
92+ "/skill on <name> - 启用 skill" ,
93+ "/skill off <name> - 禁用 skill" ,
7994 "/sh <command> - 运行受限 Linux 命令 (默认关闭)" ,
8095 "/sh --confirm <command> - 确认执行高风险命令" ,
8196 "/interrupt - 向 Codex CLI 发送 Ctrl+C" ,
8297 "/stop - 终止当前 chat 的 PTY 会话" ,
8398 "/cron_now - 立即触发一次日报推送" ,
8499 "/gh ... - GitHub skill" ,
85- "/mcp ... - MCP skill (显式调用) "
100+ "/mcp ... - MCP skill 管理与显式调用 "
86101 ] . join ( "\n" )
87102 ) ;
88103 } ) ;
89104
90105 bot . command ( "status" , async ( ctx ) => {
91106 const status = ptyManager . getStatus ( ctx . chat . id ) ;
107+ const skillStates = skillRegistry . list ( ctx . chat . id ) ;
108+ const mcpServers = skills . mcp . mcpClient . listServers ( ) ;
92109 await sendChunkedMarkdown (
93110 ctx ,
94111 [
@@ -110,7 +127,12 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
110127 ? `enabled, ${ shellManager . isReadOnly ( ) ? "read-only" : "writable" } (${ shellManager . getAllowedCommands ( ) . length } prefixes)`
111128 : "disabled"
112129 } `,
113- `mcp servers: ${ status . mcpServers . length ? status . mcpServers . join ( ", " ) : "none" } `
130+ `skills: ${ skillStates . map ( ( skill ) => `${ skill . name } :${ skill . enabled ? "on" : "off" } ` ) . join ( ", " ) || "none" } ` ,
131+ `mcp servers: ${
132+ mcpServers . length
133+ ? mcpServers . map ( ( server ) => `${ server . name } :${ server . enabled ? "on" : "off" } /${ server . connected ? "up" : "down" } ` ) . join ( ", " )
134+ : "none"
135+ } `
114136 ] . join ( "\n" )
115137 ) ;
116138 } ) ;
@@ -220,6 +242,46 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
220242 }
221243 } ) ;
222244
245+ bot . command ( "skill" , async ( ctx ) => {
246+ const payload = extractCommandPayload ( ctx . message . text , "skill" ) ;
247+ if ( ! payload || / ^ l i s t $ / i. test ( payload ) ) {
248+ await sendChunkedMarkdown (
249+ ctx ,
250+ [
251+ "Skills:" ,
252+ ...formatSkillLines ( skillRegistry . list ( ctx . chat . id ) ) ,
253+ "" ,
254+ "Usage: /skill list | /skill on <name> | /skill off <name>"
255+ ] . join ( "\n" )
256+ ) ;
257+ return ;
258+ }
259+
260+ const [ action , rawName ] = payload . split ( / \s + / , 2 ) ;
261+ if ( ! / ^ ( o n | o f f ) $ / i. test ( action ) || ! rawName ) {
262+ await sendChunkedMarkdown ( ctx , "用法: /skill list | /skill on <name> | /skill off <name>" ) ;
263+ return ;
264+ }
265+
266+ try {
267+ if ( / ^ o n $ / i. test ( action ) ) {
268+ skillRegistry . enable ( ctx . chat . id , rawName ) ;
269+ } else {
270+ skillRegistry . disable ( ctx . chat . id , rawName ) ;
271+ }
272+
273+ await sendChunkedMarkdown (
274+ ctx ,
275+ [
276+ `skill ${ rawName } 已${ / ^ o n $ / i. test ( action ) ? "启用" : "禁用" } 。` ,
277+ ...formatSkillLines ( skillRegistry . list ( ctx . chat . id ) )
278+ ] . join ( "\n" )
279+ ) ;
280+ } catch ( error ) {
281+ await sendChunkedMarkdown ( ctx , `Skill 管理失败: ${ error . message } ` ) ;
282+ }
283+ } ) ;
284+
223285 bot . command ( "new" , async ( ctx ) => {
224286 const closed = ptyManager . closeSession ( ctx . chat . id ) ;
225287 await sendChunkedMarkdown (
@@ -412,6 +474,11 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
412474 } ) ;
413475
414476 bot . command ( "gh" , async ( ctx ) => {
477+ if ( ! skillRegistry . isEnabled ( ctx . chat . id , "github" ) ) {
478+ await sendChunkedMarkdown ( ctx , "GitHub skill 当前 chat 已禁用。使用 /skill on github 重新启用。" ) ;
479+ return ;
480+ }
481+
415482 try {
416483 const text = extractCommandPayload ( ctx . message . text , "gh" ) || "help" ;
417484 const result = await skills . github . execute ( {
@@ -426,6 +493,11 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
426493 } ) ;
427494
428495 bot . command ( "mcp" , async ( ctx ) => {
496+ if ( ! skillRegistry . isEnabled ( ctx . chat . id , "mcp" ) ) {
497+ await sendChunkedMarkdown ( ctx , "MCP skill 当前 chat 已禁用。使用 /skill on mcp 重新启用。" ) ;
498+ return ;
499+ }
500+
429501 try {
430502 const text = ctx . message . text . trim ( ) ;
431503 const result = await skills . mcp . execute ( { text, ctx } ) ;
@@ -456,7 +528,9 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
456528 if ( ! text || text . startsWith ( "/" ) ) return ;
457529
458530 try {
459- const route = await router . routeMessage ( text ) ;
531+ const route = await router . routeMessage ( text , {
532+ chatId : ctx . chat . id
533+ } ) ;
460534 if ( route . target === "pty" ) {
461535 const result = await ptyManager . sendPrompt ( ctx , route . prompt ) ;
462536 if ( ! result . started ) {
@@ -476,7 +550,8 @@ export function registerHandlers({ bot, router, ptyManager, shellManager, skills
476550
477551 const result = await skill . execute ( {
478552 text : route . payload ,
479- ctx
553+ ctx,
554+ workdir : ptyManager . getStatus ( ctx . chat . id ) . workdir
480555 } ) ;
481556 await sendSkillResult ( ctx , result ) ;
482557 } catch ( error ) {
0 commit comments