@@ -16,12 +16,16 @@ import {
1616 getChannelSystemMessage ,
1717 setChannelSystemMessage ,
1818 setGitHubInfoForUser ,
19+ getUserGeneralSettings ,
20+ setUserGeneralSettings ,
1921} from "@/config" ;
2022import { startServer as startOpenCodeServer } from "@/agents/opencode" ;
2123import { startServer as startCodexServer } from "@/agents/codex" ;
2224
2325const SETTINGS_LAUNCH_ACTION = "open_settings_modal" ;
2426const SETTINGS_MODAL_ID = "settings_modal" ;
27+ const GENERAL_SETTINGS_LAUNCH_ACTION = "open_general_settings_modal" ;
28+ const GENERAL_SETTINGS_MODAL_ID = "general_settings_modal" ;
2529const GITHUB_LAUNCH_ACTION = "open_github_token_modal" ;
2630const GITHUB_MODAL_ID = "github_token_modal" ;
2731const GITHUB_TOKEN_BLOCK = "github_token" ;
@@ -40,8 +44,14 @@ const BASE_BRANCH_BLOCK = "base_branch";
4044const BASE_BRANCH_ACTION = "base_branch_input" ;
4145const CHANNEL_SYSTEM_MESSAGE_BLOCK = "channel_system_message" ;
4246const CHANNEL_SYSTEM_MESSAGE_ACTION = "channel_system_message_input" ;
47+ const GENERAL_STATUS_MESSAGE_FORMAT_BLOCK = "general_status_message_format" ;
48+ const GENERAL_STATUS_MESSAGE_FORMAT_ACTION = "general_status_message_format_select" ;
49+ const GENERAL_GIT_STRATEGY_BLOCK = "general_git_strategy" ;
50+ const GENERAL_GIT_STRATEGY_ACTION = "general_git_strategy_select" ;
4351
4452type AgentProvider = "opencode" | "claudecode" | "codex" | "kimi" | "kiro" | "qwen" ;
53+ type StatusMessageFormat = "aggressive" | "medium" | "minimum" ;
54+ type GitStrategy = "default" | "worktree" ;
4555
4656const AGENT_PROVIDERS : AgentProvider [ ] = [ "opencode" , "claudecode" , "codex" , "kimi" , "kiro" , "qwen" ] ;
4757
@@ -54,6 +64,17 @@ const AGENT_PROVIDER_LABELS: Record<AgentProvider, string> = {
5464 qwen : "Qwen Code" ,
5565} ;
5666
67+ const STATUS_MESSAGE_FORMAT_OPTIONS : Array < { label : string ; value : StatusMessageFormat } > = [
68+ { label : "Aggressive" , value : "aggressive" } ,
69+ { label : "Medium" , value : "medium" } ,
70+ { label : "Minimum" , value : "minimum" } ,
71+ ] ;
72+
73+ const GIT_STRATEGY_OPTIONS : Array < { label : string ; value : GitStrategy } > = [
74+ { label : "Worktree" , value : "worktree" } ,
75+ { label : "Default" , value : "default" } ,
76+ ] ;
77+
5778function parseAgentProvider ( value : unknown ) : AgentProvider {
5879 if ( typeof value !== "string" ) return "opencode" ;
5980 return AGENT_PROVIDERS . includes ( value as AgentProvider ) ? value as AgentProvider : "opencode" ;
@@ -284,6 +305,66 @@ function buildGitHubTokenModal(params: {
284305 } ;
285306}
286307
308+ function buildGeneralSettingsModal ( params : {
309+ channelId : string ;
310+ statusMessageFormat : StatusMessageFormat ;
311+ gitStrategy : GitStrategy ;
312+ } ) {
313+ const { channelId, statusMessageFormat, gitStrategy } = params ;
314+ const statusMessageFormatOptions = STATUS_MESSAGE_FORMAT_OPTIONS . map ( ( option ) => ( {
315+ text : { type : "plain_text" as const , text : option . label } ,
316+ value : option . value ,
317+ } ) ) ;
318+ const gitStrategyOptions = GIT_STRATEGY_OPTIONS . map ( ( option ) => ( {
319+ text : { type : "plain_text" as const , text : option . label } ,
320+ value : option . value ,
321+ } ) ) ;
322+
323+ return {
324+ type : "modal" as const ,
325+ callback_id : GENERAL_SETTINGS_MODAL_ID ,
326+ private_metadata : channelId ,
327+ title : { type : "plain_text" as const , text : "General Settings" } ,
328+ submit : { type : "plain_text" as const , text : "Save" } ,
329+ close : { type : "plain_text" as const , text : "Cancel" } ,
330+ blocks : [
331+ {
332+ type : "section" as const ,
333+ text : {
334+ type : "mrkdwn" as const ,
335+ text : "Configure default status message format and git strategy." ,
336+ } ,
337+ } ,
338+ {
339+ type : "input" as const ,
340+ block_id : GENERAL_STATUS_MESSAGE_FORMAT_BLOCK ,
341+ label : { type : "plain_text" as const , text : "Status Message Format" } ,
342+ element : {
343+ type : "static_select" as const ,
344+ action_id : GENERAL_STATUS_MESSAGE_FORMAT_ACTION ,
345+ options : statusMessageFormatOptions ,
346+ initial_option :
347+ statusMessageFormatOptions . find ( ( option ) => option . value === statusMessageFormat )
348+ ?? statusMessageFormatOptions [ 1 ] ,
349+ } ,
350+ } ,
351+ {
352+ type : "input" as const ,
353+ block_id : GENERAL_GIT_STRATEGY_BLOCK ,
354+ label : { type : "plain_text" as const , text : "Git Strategy" } ,
355+ element : {
356+ type : "static_select" as const ,
357+ action_id : GENERAL_GIT_STRATEGY_ACTION ,
358+ options : gitStrategyOptions ,
359+ initial_option :
360+ gitStrategyOptions . find ( ( option ) => option . value === gitStrategy )
361+ ?? gitStrategyOptions [ 0 ] ,
362+ } ,
363+ } ,
364+ ] ,
365+ } ;
366+ }
367+
287368export function setupInteractiveHandlers ( ) : void {
288369 for ( const slackApp of getApps ( ) ) {
289370 slackApp . action ( SETTINGS_LAUNCH_ACTION , async ( { ack, body, client } ) => {
@@ -347,6 +428,25 @@ export function setupInteractiveHandlers(): void {
347428 } ) ;
348429 } ) ;
349430
431+ slackApp . action ( GENERAL_SETTINGS_LAUNCH_ACTION , async ( { ack, body, client } ) => {
432+ await ack ( ) ;
433+
434+ const channelId = ( body as any ) . actions ?. [ 0 ] ?. value
435+ ?? ( body as any ) . channel ?. id
436+ ?? "" ;
437+ const generalSettings = getUserGeneralSettings ( ) ;
438+ const view = buildGeneralSettingsModal ( {
439+ channelId,
440+ statusMessageFormat : generalSettings . defaultStatusMessageFormat ,
441+ gitStrategy : generalSettings . gitStrategy ,
442+ } ) ;
443+
444+ await client . views . open ( {
445+ trigger_id : ( body as any ) . trigger_id ,
446+ view,
447+ } ) ;
448+ } ) ;
449+
350450 slackApp . action ( PROVIDER_ACTION , async ( { ack, body, client } ) => {
351451 await ack ( ) ;
352452
@@ -528,6 +628,50 @@ export function setupInteractiveHandlers(): void {
528628 } ) ;
529629 } ) ;
530630
631+ slackApp . view ( GENERAL_SETTINGS_MODAL_ID , async ( { ack, view, body, client } ) => {
632+ const values = view . state . values ;
633+ const selectedStatusMessageFormat = values ?. [ GENERAL_STATUS_MESSAGE_FORMAT_BLOCK ] ?. [ GENERAL_STATUS_MESSAGE_FORMAT_ACTION ] ?. selected_option ?. value ;
634+ const selectedGitStrategy = values ?. [ GENERAL_GIT_STRATEGY_BLOCK ] ?. [ GENERAL_GIT_STRATEGY_ACTION ] ?. selected_option ?. value ;
635+
636+ const statusMessageFormat : StatusMessageFormat =
637+ selectedStatusMessageFormat === "aggressive"
638+ || selectedStatusMessageFormat === "minimum"
639+ || selectedStatusMessageFormat === "medium"
640+ ? selectedStatusMessageFormat
641+ : "medium" ;
642+ const gitStrategy : GitStrategy = selectedGitStrategy === "default" ? "default" : "worktree" ;
643+
644+ await ack ( ) ;
645+
646+ try {
647+ setUserGeneralSettings ( {
648+ defaultStatusMessageFormat : statusMessageFormat ,
649+ gitStrategy,
650+ } ) ;
651+ } catch ( err ) {
652+ const userId = ( body as any ) . user ?. id ;
653+ const channelId = view . private_metadata || ( body as any ) . channel ?. id ;
654+ if ( userId && channelId ) {
655+ await client . chat . postEphemeral ( {
656+ channel : channelId ,
657+ user : userId ,
658+ text : `Failed to update general settings: ${ err instanceof Error ? err . message : String ( err ) } ` ,
659+ } ) ;
660+ }
661+ return ;
662+ }
663+
664+ const userId = ( body as any ) . user ?. id ;
665+ const channelId = view . private_metadata || ( body as any ) . channel ?. id ;
666+ if ( userId && channelId ) {
667+ await client . chat . postEphemeral ( {
668+ channel : channelId ,
669+ user : userId ,
670+ text : "General settings updated." ,
671+ } ) ;
672+ }
673+ } ) ;
674+
531675 // Handle user choice button clicks (from Ode ask_user actions)
532676 slackApp . action ( / ^ u s e r _ c h o i c e _ \d + $ / , async ( { ack, body, client } ) => {
533677 await ack ( ) ;
0 commit comments