@@ -9,11 +9,13 @@ import { join } from 'path';
99import {
1010 loadSMSConfig ,
1111 saveSMSConfig ,
12+ sendNotification ,
1213 sendSMSNotification ,
1314 notifyReviewReady ,
1415 notifyWithYesNo ,
1516 notifyTaskComplete ,
1617 cleanupExpiredPrompts ,
18+ type MessageChannel ,
1719} from '../../hooks/sms-notify.js' ;
1820import {
1921 loadActionQueue ,
@@ -34,17 +36,32 @@ export function createSMSNotifyCommand(): Command {
3436 `
3537Setup (optional):
3638 1. Create Twilio account at https://twilio.com
37- 2. Get Account SID, Auth Token, and phone number
39+ 2. Get Account SID, Auth Token, and phone numbers
3840 3. Set environment variables:
3941 export TWILIO_ACCOUNT_SID=your_sid
4042 export TWILIO_AUTH_TOKEN=your_token
43+
44+ For WhatsApp (recommended - cheaper for conversations):
45+ export TWILIO_WHATSAPP_FROM=+1234567890
46+ export TWILIO_WHATSAPP_TO=+1234567890
47+ export TWILIO_CHANNEL=whatsapp
48+
49+ For SMS:
50+ export TWILIO_SMS_FROM=+1234567890
51+ export TWILIO_SMS_TO=+1234567890
52+ export TWILIO_CHANNEL=sms
53+
54+ Legacy (works for both, defaults to WhatsApp):
4155 export TWILIO_FROM_NUMBER=+1234567890
4256 export TWILIO_TO_NUMBER=+1234567890
57+
4358 4. Enable: stackmemory notify enable
4459
4560Examples:
4661 stackmemory notify status Check configuration
4762 stackmemory notify enable Enable notifications
63+ stackmemory notify channel whatsapp Switch to WhatsApp
64+ stackmemory notify channel sms Switch to SMS
4865 stackmemory notify test Send test message
4966 stackmemory notify send "PR ready" Send custom message
5067 stackmemory notify review "PR #123" Send review notification with options
@@ -58,28 +75,57 @@ Examples:
5875 . action ( ( ) => {
5976 const config = loadSMSConfig ( ) ;
6077
61- console . log ( chalk . blue ( 'SMS Notification Status:' ) ) ;
78+ console . log ( chalk . blue ( 'Notification Status:' ) ) ;
6279 console . log ( ) ;
6380
64- // Check if configured
65- const hasCreds =
66- config . accountSid &&
67- config . authToken &&
68- config . fromNumber &&
81+ // Check credentials
82+ const hasCreds = config . accountSid && config . authToken ;
83+
84+ // Check channel-specific numbers
85+ const channel = config . channel || 'whatsapp' ;
86+ const hasWhatsApp =
87+ config . whatsappFromNumber ||
88+ config . fromNumber ||
89+ config . whatsappToNumber ||
90+ config . toNumber ;
91+ const hasSMS =
92+ config . smsFromNumber ||
93+ config . fromNumber ||
94+ config . smsToNumber ||
6995 config . toNumber ;
96+ const hasNumbers = channel === 'whatsapp' ? hasWhatsApp : hasSMS ;
7097
7198 console . log (
7299 ` ${ chalk . gray ( 'Enabled:' ) } ${ config . enabled ? chalk . green ( 'yes' ) : chalk . red ( 'no' ) } `
73100 ) ;
74101 console . log (
75- ` ${ chalk . gray ( 'Configured:' ) } ${ hasCreds ? chalk . green ( 'yes' ) : chalk . yellow ( 'no (set env vars)' ) } `
102+ ` ${ chalk . gray ( 'Channel:' ) } ${ channel === 'whatsapp' ? chalk . cyan ( 'WhatsApp' ) : chalk . blue ( 'SMS' ) } `
103+ ) ;
104+ console . log (
105+ ` ${ chalk . gray ( 'Configured:' ) } ${ hasCreds && hasNumbers ? chalk . green ( 'yes' ) : chalk . yellow ( 'no (set env vars)' ) } `
76106 ) ;
77107
78- if ( config . fromNumber ) {
79- console . log ( ` ${ chalk . gray ( 'From:' ) } ${ maskPhone ( config . fromNumber ) } ` ) ;
80- }
81- if ( config . toNumber ) {
82- console . log ( ` ${ chalk . gray ( 'To:' ) } ${ maskPhone ( config . toNumber ) } ` ) ;
108+ // Show channel-specific numbers
109+ console . log ( ) ;
110+ console . log ( chalk . blue ( 'Numbers:' ) ) ;
111+ if ( channel === 'whatsapp' ) {
112+ const from = config . whatsappFromNumber || config . fromNumber ;
113+ const to = config . whatsappToNumber || config . toNumber ;
114+ if ( from ) {
115+ console . log ( ` ${ chalk . gray ( 'WhatsApp From:' ) } ${ maskPhone ( from ) } ` ) ;
116+ }
117+ if ( to ) {
118+ console . log ( ` ${ chalk . gray ( 'WhatsApp To:' ) } ${ maskPhone ( to ) } ` ) ;
119+ }
120+ } else {
121+ const from = config . smsFromNumber || config . fromNumber ;
122+ const to = config . smsToNumber || config . toNumber ;
123+ if ( from ) {
124+ console . log ( ` ${ chalk . gray ( 'SMS From:' ) } ${ maskPhone ( from ) } ` ) ;
125+ }
126+ if ( to ) {
127+ console . log ( ` ${ chalk . gray ( 'SMS To:' ) } ${ maskPhone ( to ) } ` ) ;
128+ }
83129 }
84130
85131 console . log ( ) ;
@@ -111,15 +157,21 @@ Examples:
111157 ` ${ chalk . gray ( 'Response Timeout:' ) } ${ config . responseTimeout } s`
112158 ) ;
113159
114- if ( ! hasCreds ) {
160+ if ( ! hasCreds || ! hasNumbers ) {
115161 console . log ( ) ;
116162 console . log (
117163 chalk . yellow ( 'To configure, set these environment variables:' )
118164 ) ;
119165 console . log ( chalk . gray ( ' export TWILIO_ACCOUNT_SID=your_sid' ) ) ;
120166 console . log ( chalk . gray ( ' export TWILIO_AUTH_TOKEN=your_token' ) ) ;
121- console . log ( chalk . gray ( ' export TWILIO_FROM_NUMBER=+1234567890' ) ) ;
122- console . log ( chalk . gray ( ' export TWILIO_TO_NUMBER=+1234567890' ) ) ;
167+ console . log ( ) ;
168+ console . log ( chalk . gray ( ' For WhatsApp (recommended):' ) ) ;
169+ console . log ( chalk . gray ( ' export TWILIO_WHATSAPP_FROM=+1234567890' ) ) ;
170+ console . log ( chalk . gray ( ' export TWILIO_WHATSAPP_TO=+1234567890' ) ) ;
171+ console . log ( ) ;
172+ console . log ( chalk . gray ( ' For SMS:' ) ) ;
173+ console . log ( chalk . gray ( ' export TWILIO_SMS_FROM=+1234567890' ) ) ;
174+ console . log ( chalk . gray ( ' export TWILIO_SMS_TO=+1234567890' ) ) ;
123175 }
124176 } ) ;
125177
@@ -156,20 +208,74 @@ Examples:
156208 console . log ( chalk . yellow ( 'SMS notifications disabled' ) ) ;
157209 } ) ;
158210
211+ cmd
212+ . command ( 'channel <type>' )
213+ . description ( 'Set notification channel (whatsapp|sms)' )
214+ . action ( ( type : string ) => {
215+ const validChannels : MessageChannel [ ] = [ 'whatsapp' , 'sms' ] ;
216+ const channel = type . toLowerCase ( ) as MessageChannel ;
217+
218+ if ( ! validChannels . includes ( channel ) ) {
219+ console . log (
220+ chalk . red ( `Invalid channel. Use: ${ validChannels . join ( ', ' ) } ` )
221+ ) ;
222+ return ;
223+ }
224+
225+ const config = loadSMSConfig ( ) ;
226+ config . channel = channel ;
227+ saveSMSConfig ( config ) ;
228+
229+ const label = channel === 'whatsapp' ? 'WhatsApp' : 'SMS' ;
230+ console . log ( chalk . green ( `Notification channel set to ${ label } ` ) ) ;
231+
232+ // Show relevant env vars
233+ if ( channel === 'whatsapp' ) {
234+ const hasNumbers = config . whatsappFromNumber || config . fromNumber ;
235+ if ( ! hasNumbers ) {
236+ console . log (
237+ chalk . yellow ( 'Set TWILIO_WHATSAPP_FROM and TWILIO_WHATSAPP_TO' )
238+ ) ;
239+ }
240+ } else {
241+ const hasNumbers = config . smsFromNumber || config . fromNumber ;
242+ if ( ! hasNumbers ) {
243+ console . log ( chalk . yellow ( 'Set TWILIO_SMS_FROM and TWILIO_SMS_TO' ) ) ;
244+ }
245+ }
246+ } ) ;
247+
159248 cmd
160249 . command ( 'test' )
161250 . description ( 'Send a test notification' )
162- . action ( async ( ) => {
163- console . log ( chalk . blue ( 'Sending test notification...' ) ) ;
251+ . option ( '--sms' , 'Force SMS channel' )
252+ . option ( '--whatsapp' , 'Force WhatsApp channel' )
253+ . action ( async ( options : { sms ?: boolean ; whatsapp ?: boolean } ) => {
254+ const config = loadSMSConfig ( ) ;
255+ const channelOverride : MessageChannel | undefined = options . sms
256+ ? 'sms'
257+ : options . whatsapp
258+ ? 'whatsapp'
259+ : undefined ;
260+ const channelLabel =
261+ channelOverride || config . channel === 'whatsapp' ? 'WhatsApp' : 'SMS' ;
164262
165- const result = await sendSMSNotification ( {
166- type : 'custom' ,
167- title : 'StackMemory Test' ,
168- message : 'This is a test notification from StackMemory.' ,
169- } ) ;
263+ console . log (
264+ chalk . blue ( `Sending test notification via ${ channelLabel } ...` )
265+ ) ;
266+
267+ const result = await sendNotification (
268+ {
269+ type : 'custom' ,
270+ title : 'StackMemory Test' ,
271+ message : 'This is a test notification from StackMemory.' ,
272+ } ,
273+ channelOverride
274+ ) ;
170275
171276 if ( result . success ) {
172- console . log ( chalk . green ( 'Test message sent successfully!' ) ) ;
277+ const usedChannel = result . channel === 'whatsapp' ? 'WhatsApp' : 'SMS' ;
278+ console . log ( chalk . green ( `Test message sent via ${ usedChannel } !` ) ) ;
173279 } else {
174280 console . log ( chalk . red ( `Failed: ${ result . error } ` ) ) ;
175281 }
0 commit comments