@stackmemoryai/stackmemory 0.5.12 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/settings.js +306 -0
- package/dist/cli/commands/settings.js.map +7 -0
- package/dist/cli/commands/sms-notify.js +32 -0
- package/dist/cli/commands/sms-notify.js.map +2 -2
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/hooks/sms-notify.js +54 -0
- package/dist/hooks/sms-notify.js.map +2 -2
- package/dist/hooks/sms-watcher.js +93 -0
- package/dist/hooks/sms-watcher.js.map +7 -0
- package/dist/hooks/sms-webhook.js +57 -11
- package/dist/hooks/sms-webhook.js.map +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
import { existsSync, writeFileSync, mkdirSync, readFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import {
|
|
12
|
+
loadSMSConfig,
|
|
13
|
+
saveSMSConfig,
|
|
14
|
+
getMissingConfig
|
|
15
|
+
} from "../../hooks/sms-notify.js";
|
|
16
|
+
function createSettingsCommand() {
|
|
17
|
+
const cmd = new Command("settings").description("View and configure StackMemory settings").addHelpText(
|
|
18
|
+
"after",
|
|
19
|
+
`
|
|
20
|
+
Examples:
|
|
21
|
+
stackmemory settings Show all settings and missing config
|
|
22
|
+
stackmemory settings notifications Configure notifications interactively
|
|
23
|
+
stackmemory settings env Show required environment variables
|
|
24
|
+
`
|
|
25
|
+
);
|
|
26
|
+
cmd.command("show").description("Show current settings and what is missing").action(() => {
|
|
27
|
+
showSettings();
|
|
28
|
+
});
|
|
29
|
+
cmd.command("notifications").alias("notify").description("Configure notifications interactively").action(async () => {
|
|
30
|
+
await configureNotifications();
|
|
31
|
+
});
|
|
32
|
+
cmd.command("env").description("Show required environment variables").action(() => {
|
|
33
|
+
showEnvVars();
|
|
34
|
+
});
|
|
35
|
+
cmd.action(() => {
|
|
36
|
+
showSettings();
|
|
37
|
+
});
|
|
38
|
+
return cmd;
|
|
39
|
+
}
|
|
40
|
+
function showSettings() {
|
|
41
|
+
console.log(chalk.blue.bold("\nStackMemory Settings\n"));
|
|
42
|
+
const config = loadSMSConfig();
|
|
43
|
+
const { missing, configured, ready } = getMissingConfig();
|
|
44
|
+
console.log(chalk.cyan("Notifications:"));
|
|
45
|
+
console.log(
|
|
46
|
+
` ${chalk.gray("Enabled:")} ${config.enabled ? chalk.green("yes") : chalk.yellow("no")}`
|
|
47
|
+
);
|
|
48
|
+
console.log(
|
|
49
|
+
` ${chalk.gray("Channel:")} ${config.channel === "whatsapp" ? chalk.cyan("WhatsApp") : chalk.blue("SMS")}`
|
|
50
|
+
);
|
|
51
|
+
console.log(
|
|
52
|
+
` ${chalk.gray("Ready:")} ${ready ? chalk.green("yes") : chalk.red("no")}`
|
|
53
|
+
);
|
|
54
|
+
if (configured.length > 0) {
|
|
55
|
+
console.log(`
|
|
56
|
+
${chalk.green("Configured:")}`);
|
|
57
|
+
configured.forEach((item) => {
|
|
58
|
+
console.log(` ${chalk.green("\u2713")} ${item}`);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (missing.length > 0) {
|
|
62
|
+
console.log(`
|
|
63
|
+
${chalk.red("Missing:")}`);
|
|
64
|
+
missing.forEach((item) => {
|
|
65
|
+
console.log(` ${chalk.red("\u2717")} ${item}`);
|
|
66
|
+
});
|
|
67
|
+
console.log(
|
|
68
|
+
chalk.yellow('\n Run "stackmemory settings notifications" to configure')
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const ngrokUrlPath = join(homedir(), ".stackmemory", "ngrok-url.txt");
|
|
72
|
+
if (existsSync(ngrokUrlPath)) {
|
|
73
|
+
const ngrokUrl = readFileSync(ngrokUrlPath, "utf8").trim();
|
|
74
|
+
console.log(`
|
|
75
|
+
${chalk.gray("Webhook URL:")} ${ngrokUrl}/sms/incoming`);
|
|
76
|
+
}
|
|
77
|
+
console.log();
|
|
78
|
+
}
|
|
79
|
+
function showEnvVars() {
|
|
80
|
+
console.log(chalk.blue.bold("\nRequired Environment Variables\n"));
|
|
81
|
+
const { missing, configured } = getMissingConfig();
|
|
82
|
+
const config = loadSMSConfig();
|
|
83
|
+
console.log(chalk.cyan("Twilio Credentials (required):"));
|
|
84
|
+
console.log(
|
|
85
|
+
` ${configured.includes("TWILIO_ACCOUNT_SID") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_ACCOUNT_SID`
|
|
86
|
+
);
|
|
87
|
+
console.log(
|
|
88
|
+
` ${configured.includes("TWILIO_AUTH_TOKEN") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_AUTH_TOKEN`
|
|
89
|
+
);
|
|
90
|
+
console.log(
|
|
91
|
+
chalk.cyan(
|
|
92
|
+
`
|
|
93
|
+
${config.channel === "whatsapp" ? "WhatsApp" : "SMS"} Numbers:`
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
if (config.channel === "whatsapp") {
|
|
97
|
+
console.log(
|
|
98
|
+
` ${configured.includes("TWILIO_WHATSAPP_FROM") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_WHATSAPP_FROM`
|
|
99
|
+
);
|
|
100
|
+
console.log(
|
|
101
|
+
` ${configured.includes("TWILIO_WHATSAPP_TO") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_WHATSAPP_TO`
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
console.log(
|
|
105
|
+
` ${configured.includes("TWILIO_SMS_FROM") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_SMS_FROM`
|
|
106
|
+
);
|
|
107
|
+
console.log(
|
|
108
|
+
` ${configured.includes("TWILIO_SMS_TO") ? chalk.green("\u2713") : chalk.red("\u2717")} TWILIO_SMS_TO`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (missing.length > 0) {
|
|
112
|
+
console.log(chalk.yellow("\nAdd to your .env file or shell profile:"));
|
|
113
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
114
|
+
if (missing.includes("TWILIO_ACCOUNT_SID")) {
|
|
115
|
+
console.log('export TWILIO_ACCOUNT_SID="your_account_sid"');
|
|
116
|
+
}
|
|
117
|
+
if (missing.includes("TWILIO_AUTH_TOKEN")) {
|
|
118
|
+
console.log('export TWILIO_AUTH_TOKEN="your_auth_token"');
|
|
119
|
+
}
|
|
120
|
+
if (missing.includes("TWILIO_WHATSAPP_FROM")) {
|
|
121
|
+
console.log(
|
|
122
|
+
'export TWILIO_WHATSAPP_FROM="+14155238886" # Twilio sandbox'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (missing.includes("TWILIO_WHATSAPP_TO")) {
|
|
126
|
+
console.log('export TWILIO_WHATSAPP_TO="+1234567890" # Your phone');
|
|
127
|
+
}
|
|
128
|
+
if (missing.includes("TWILIO_SMS_FROM")) {
|
|
129
|
+
console.log('export TWILIO_SMS_FROM="+1234567890" # Twilio number');
|
|
130
|
+
}
|
|
131
|
+
if (missing.includes("TWILIO_SMS_TO")) {
|
|
132
|
+
console.log('export TWILIO_SMS_TO="+1234567890" # Your phone');
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
135
|
+
}
|
|
136
|
+
console.log();
|
|
137
|
+
}
|
|
138
|
+
async function configureNotifications() {
|
|
139
|
+
console.log(chalk.blue.bold("\nNotification Setup\n"));
|
|
140
|
+
const config = loadSMSConfig();
|
|
141
|
+
const { missing } = getMissingConfig();
|
|
142
|
+
const { enable } = await inquirer.prompt([
|
|
143
|
+
{
|
|
144
|
+
type: "confirm",
|
|
145
|
+
name: "enable",
|
|
146
|
+
message: "Enable SMS/WhatsApp notifications?",
|
|
147
|
+
default: config.enabled
|
|
148
|
+
}
|
|
149
|
+
]);
|
|
150
|
+
if (!enable) {
|
|
151
|
+
config.enabled = false;
|
|
152
|
+
saveSMSConfig(config);
|
|
153
|
+
console.log(chalk.yellow("Notifications disabled"));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const { channel } = await inquirer.prompt([
|
|
157
|
+
{
|
|
158
|
+
type: "list",
|
|
159
|
+
name: "channel",
|
|
160
|
+
message: "Which channel do you want to use?",
|
|
161
|
+
choices: [
|
|
162
|
+
{
|
|
163
|
+
name: "WhatsApp (recommended - cheaper for conversations)",
|
|
164
|
+
value: "whatsapp"
|
|
165
|
+
},
|
|
166
|
+
{ name: "SMS (requires A2P 10DLC registration for US)", value: "sms" }
|
|
167
|
+
],
|
|
168
|
+
default: config.channel
|
|
169
|
+
}
|
|
170
|
+
]);
|
|
171
|
+
config.channel = channel;
|
|
172
|
+
if (missing.includes("TWILIO_ACCOUNT_SID") || missing.includes("TWILIO_AUTH_TOKEN")) {
|
|
173
|
+
console.log(chalk.yellow("\nTwilio credentials not found in environment."));
|
|
174
|
+
const { hasAccount } = await inquirer.prompt([
|
|
175
|
+
{
|
|
176
|
+
type: "confirm",
|
|
177
|
+
name: "hasAccount",
|
|
178
|
+
message: "Do you have a Twilio account?",
|
|
179
|
+
default: true
|
|
180
|
+
}
|
|
181
|
+
]);
|
|
182
|
+
if (!hasAccount) {
|
|
183
|
+
console.log(chalk.cyan("\nCreate a free Twilio account:"));
|
|
184
|
+
console.log(" https://www.twilio.com/try-twilio\n");
|
|
185
|
+
console.log("Then run this command again.");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const { saveToEnv } = await inquirer.prompt([
|
|
189
|
+
{
|
|
190
|
+
type: "confirm",
|
|
191
|
+
name: "saveToEnv",
|
|
192
|
+
message: "Would you like to save credentials to ~/.stackmemory/.env?",
|
|
193
|
+
default: true
|
|
194
|
+
}
|
|
195
|
+
]);
|
|
196
|
+
if (saveToEnv) {
|
|
197
|
+
const { accountSid, authToken } = await inquirer.prompt([
|
|
198
|
+
{
|
|
199
|
+
type: "input",
|
|
200
|
+
name: "accountSid",
|
|
201
|
+
message: "Twilio Account SID:",
|
|
202
|
+
validate: (input) => input.startsWith("AC") ? true : "Account SID should start with AC"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
type: "password",
|
|
206
|
+
name: "authToken",
|
|
207
|
+
message: "Twilio Auth Token:",
|
|
208
|
+
mask: "*"
|
|
209
|
+
}
|
|
210
|
+
]);
|
|
211
|
+
saveToEnvFile({
|
|
212
|
+
TWILIO_ACCOUNT_SID: accountSid,
|
|
213
|
+
TWILIO_AUTH_TOKEN: authToken
|
|
214
|
+
});
|
|
215
|
+
console.log(chalk.green("Credentials saved to ~/.stackmemory/.env"));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (channel === "whatsapp") {
|
|
219
|
+
console.log(chalk.cyan("\nWhatsApp Setup:"));
|
|
220
|
+
console.log(
|
|
221
|
+
" 1. Go to: https://console.twilio.com/us1/develop/sms/try-it-out/whatsapp-learn"
|
|
222
|
+
);
|
|
223
|
+
console.log(" 2. Note the sandbox number (e.g., +14155238886)");
|
|
224
|
+
console.log(" 3. Send the join code from your phone\n");
|
|
225
|
+
const { whatsappFrom, whatsappTo } = await inquirer.prompt([
|
|
226
|
+
{
|
|
227
|
+
type: "input",
|
|
228
|
+
name: "whatsappFrom",
|
|
229
|
+
message: "Twilio WhatsApp number (sandbox):",
|
|
230
|
+
default: config.whatsappFromNumber || "+14155238886"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: "input",
|
|
234
|
+
name: "whatsappTo",
|
|
235
|
+
message: "Your phone number:",
|
|
236
|
+
default: config.whatsappToNumber,
|
|
237
|
+
validate: (input) => input.startsWith("+") ? true : "Include country code (e.g., +1234567890)"
|
|
238
|
+
}
|
|
239
|
+
]);
|
|
240
|
+
saveToEnvFile({
|
|
241
|
+
TWILIO_WHATSAPP_FROM: whatsappFrom,
|
|
242
|
+
TWILIO_WHATSAPP_TO: whatsappTo,
|
|
243
|
+
TWILIO_CHANNEL: "whatsapp"
|
|
244
|
+
});
|
|
245
|
+
} else {
|
|
246
|
+
console.log(chalk.cyan("\nSMS Setup:"));
|
|
247
|
+
console.log(
|
|
248
|
+
chalk.yellow(" Note: US carriers require A2P 10DLC registration")
|
|
249
|
+
);
|
|
250
|
+
console.log(
|
|
251
|
+
" Register at: https://console.twilio.com/us1/develop/sms/settings/compliance\n"
|
|
252
|
+
);
|
|
253
|
+
const { smsFrom, smsTo } = await inquirer.prompt([
|
|
254
|
+
{
|
|
255
|
+
type: "input",
|
|
256
|
+
name: "smsFrom",
|
|
257
|
+
message: "Twilio SMS number:",
|
|
258
|
+
default: config.smsFromNumber
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
type: "input",
|
|
262
|
+
name: "smsTo",
|
|
263
|
+
message: "Your phone number:",
|
|
264
|
+
default: config.smsToNumber,
|
|
265
|
+
validate: (input) => input.startsWith("+") ? true : "Include country code (e.g., +1234567890)"
|
|
266
|
+
}
|
|
267
|
+
]);
|
|
268
|
+
saveToEnvFile({
|
|
269
|
+
TWILIO_SMS_FROM: smsFrom,
|
|
270
|
+
TWILIO_SMS_TO: smsTo,
|
|
271
|
+
TWILIO_CHANNEL: "sms"
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
config.enabled = true;
|
|
275
|
+
saveSMSConfig(config);
|
|
276
|
+
console.log(chalk.green("\nNotifications configured!"));
|
|
277
|
+
console.log(chalk.gray("Test with: stackmemory notify test"));
|
|
278
|
+
}
|
|
279
|
+
function saveToEnvFile(vars) {
|
|
280
|
+
const envDir = join(homedir(), ".stackmemory");
|
|
281
|
+
const envPath = join(envDir, ".env");
|
|
282
|
+
if (!existsSync(envDir)) {
|
|
283
|
+
mkdirSync(envDir, { recursive: true });
|
|
284
|
+
}
|
|
285
|
+
let content = "";
|
|
286
|
+
if (existsSync(envPath)) {
|
|
287
|
+
content = readFileSync(envPath, "utf8");
|
|
288
|
+
}
|
|
289
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
290
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
291
|
+
const line = `${key}="${value}"`;
|
|
292
|
+
if (regex.test(content)) {
|
|
293
|
+
content = content.replace(regex, line);
|
|
294
|
+
} else {
|
|
295
|
+
content += `${content.endsWith("\n") || content === "" ? "" : "\n"}${line}
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
writeFileSync(envPath, content);
|
|
300
|
+
}
|
|
301
|
+
var settings_default = createSettingsCommand;
|
|
302
|
+
export {
|
|
303
|
+
createSettingsCommand,
|
|
304
|
+
settings_default as default
|
|
305
|
+
};
|
|
306
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/cli/commands/settings.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * CLI command for viewing and configuring StackMemory settings\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport inquirer from 'inquirer';\nimport { existsSync, writeFileSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport {\n loadSMSConfig,\n saveSMSConfig,\n getMissingConfig,\n type MessageChannel,\n} from '../../hooks/sms-notify.js';\n\nexport function createSettingsCommand(): Command {\n const cmd = new Command('settings')\n .description('View and configure StackMemory settings')\n .addHelpText(\n 'after',\n `\nExamples:\n stackmemory settings Show all settings and missing config\n stackmemory settings notifications Configure notifications interactively\n stackmemory settings env Show required environment variables\n`\n );\n\n cmd\n .command('show')\n .description('Show current settings and what is missing')\n .action(() => {\n showSettings();\n });\n\n cmd\n .command('notifications')\n .alias('notify')\n .description('Configure notifications interactively')\n .action(async () => {\n await configureNotifications();\n });\n\n cmd\n .command('env')\n .description('Show required environment variables')\n .action(() => {\n showEnvVars();\n });\n\n // Default action - show settings\n cmd.action(() => {\n showSettings();\n });\n\n return cmd;\n}\n\nfunction showSettings(): void {\n console.log(chalk.blue.bold('\\nStackMemory Settings\\n'));\n\n // Notification settings\n const config = loadSMSConfig();\n const { missing, configured, ready } = getMissingConfig();\n\n console.log(chalk.cyan('Notifications:'));\n console.log(\n ` ${chalk.gray('Enabled:')} ${config.enabled ? chalk.green('yes') : chalk.yellow('no')}`\n );\n console.log(\n ` ${chalk.gray('Channel:')} ${config.channel === 'whatsapp' ? chalk.cyan('WhatsApp') : chalk.blue('SMS')}`\n );\n console.log(\n ` ${chalk.gray('Ready:')} ${ready ? chalk.green('yes') : chalk.red('no')}`\n );\n\n if (configured.length > 0) {\n console.log(`\\n ${chalk.green('Configured:')}`);\n configured.forEach((item) => {\n console.log(` ${chalk.green('\u2713')} ${item}`);\n });\n }\n\n if (missing.length > 0) {\n console.log(`\\n ${chalk.red('Missing:')}`);\n missing.forEach((item) => {\n console.log(` ${chalk.red('\u2717')} ${item}`);\n });\n\n console.log(\n chalk.yellow('\\n Run \"stackmemory settings notifications\" to configure')\n );\n }\n\n // Show ngrok URL if available\n const ngrokUrlPath = join(homedir(), '.stackmemory', 'ngrok-url.txt');\n if (existsSync(ngrokUrlPath)) {\n const ngrokUrl = readFileSync(ngrokUrlPath, 'utf8').trim();\n console.log(`\\n ${chalk.gray('Webhook URL:')} ${ngrokUrl}/sms/incoming`);\n }\n\n console.log();\n}\n\nfunction showEnvVars(): void {\n console.log(chalk.blue.bold('\\nRequired Environment Variables\\n'));\n\n const { missing, configured } = getMissingConfig();\n const config = loadSMSConfig();\n\n console.log(chalk.cyan('Twilio Credentials (required):'));\n console.log(\n ` ${configured.includes('TWILIO_ACCOUNT_SID') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_ACCOUNT_SID`\n );\n console.log(\n ` ${configured.includes('TWILIO_AUTH_TOKEN') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_AUTH_TOKEN`\n );\n\n console.log(\n chalk.cyan(\n `\\n${config.channel === 'whatsapp' ? 'WhatsApp' : 'SMS'} Numbers:`\n )\n );\n if (config.channel === 'whatsapp') {\n console.log(\n ` ${configured.includes('TWILIO_WHATSAPP_FROM') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_WHATSAPP_FROM`\n );\n console.log(\n ` ${configured.includes('TWILIO_WHATSAPP_TO') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_WHATSAPP_TO`\n );\n } else {\n console.log(\n ` ${configured.includes('TWILIO_SMS_FROM') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_SMS_FROM`\n );\n console.log(\n ` ${configured.includes('TWILIO_SMS_TO') ? chalk.green('\u2713') : chalk.red('\u2717')} TWILIO_SMS_TO`\n );\n }\n\n if (missing.length > 0) {\n console.log(chalk.yellow('\\nAdd to your .env file or shell profile:'));\n console.log(chalk.gray('\u2500'.repeat(50)));\n\n if (missing.includes('TWILIO_ACCOUNT_SID')) {\n console.log('export TWILIO_ACCOUNT_SID=\"your_account_sid\"');\n }\n if (missing.includes('TWILIO_AUTH_TOKEN')) {\n console.log('export TWILIO_AUTH_TOKEN=\"your_auth_token\"');\n }\n if (missing.includes('TWILIO_WHATSAPP_FROM')) {\n console.log(\n 'export TWILIO_WHATSAPP_FROM=\"+14155238886\" # Twilio sandbox'\n );\n }\n if (missing.includes('TWILIO_WHATSAPP_TO')) {\n console.log('export TWILIO_WHATSAPP_TO=\"+1234567890\" # Your phone');\n }\n if (missing.includes('TWILIO_SMS_FROM')) {\n console.log('export TWILIO_SMS_FROM=\"+1234567890\" # Twilio number');\n }\n if (missing.includes('TWILIO_SMS_TO')) {\n console.log('export TWILIO_SMS_TO=\"+1234567890\" # Your phone');\n }\n\n console.log(chalk.gray('\u2500'.repeat(50)));\n }\n\n console.log();\n}\n\nasync function configureNotifications(): Promise<void> {\n console.log(chalk.blue.bold('\\nNotification Setup\\n'));\n\n const config = loadSMSConfig();\n const { missing } = getMissingConfig();\n\n // Ask if they want to enable\n const { enable } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'enable',\n message: 'Enable SMS/WhatsApp notifications?',\n default: config.enabled,\n },\n ]);\n\n if (!enable) {\n config.enabled = false;\n saveSMSConfig(config);\n console.log(chalk.yellow('Notifications disabled'));\n return;\n }\n\n // Choose channel\n const { channel } = await inquirer.prompt([\n {\n type: 'list',\n name: 'channel',\n message: 'Which channel do you want to use?',\n choices: [\n {\n name: 'WhatsApp (recommended - cheaper for conversations)',\n value: 'whatsapp',\n },\n { name: 'SMS (requires A2P 10DLC registration for US)', value: 'sms' },\n ],\n default: config.channel,\n },\n ]);\n\n config.channel = channel as MessageChannel;\n\n // Check for missing credentials\n if (\n missing.includes('TWILIO_ACCOUNT_SID') ||\n missing.includes('TWILIO_AUTH_TOKEN')\n ) {\n console.log(chalk.yellow('\\nTwilio credentials not found in environment.'));\n\n const { hasAccount } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'hasAccount',\n message: 'Do you have a Twilio account?',\n default: true,\n },\n ]);\n\n if (!hasAccount) {\n console.log(chalk.cyan('\\nCreate a free Twilio account:'));\n console.log(' https://www.twilio.com/try-twilio\\n');\n console.log('Then run this command again.');\n return;\n }\n\n const { saveToEnv } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'saveToEnv',\n message: 'Would you like to save credentials to ~/.stackmemory/.env?',\n default: true,\n },\n ]);\n\n if (saveToEnv) {\n const { accountSid, authToken } = await inquirer.prompt([\n {\n type: 'input',\n name: 'accountSid',\n message: 'Twilio Account SID:',\n validate: (input: string) =>\n input.startsWith('AC') ? true : 'Account SID should start with AC',\n },\n {\n type: 'password',\n name: 'authToken',\n message: 'Twilio Auth Token:',\n mask: '*',\n },\n ]);\n\n saveToEnvFile({\n TWILIO_ACCOUNT_SID: accountSid,\n TWILIO_AUTH_TOKEN: authToken,\n });\n console.log(chalk.green('Credentials saved to ~/.stackmemory/.env'));\n }\n }\n\n // Get phone numbers\n if (channel === 'whatsapp') {\n console.log(chalk.cyan('\\nWhatsApp Setup:'));\n console.log(\n ' 1. Go to: https://console.twilio.com/us1/develop/sms/try-it-out/whatsapp-learn'\n );\n console.log(' 2. Note the sandbox number (e.g., +14155238886)');\n console.log(' 3. Send the join code from your phone\\n');\n\n const { whatsappFrom, whatsappTo } = await inquirer.prompt([\n {\n type: 'input',\n name: 'whatsappFrom',\n message: 'Twilio WhatsApp number (sandbox):',\n default: config.whatsappFromNumber || '+14155238886',\n },\n {\n type: 'input',\n name: 'whatsappTo',\n message: 'Your phone number:',\n default: config.whatsappToNumber,\n validate: (input: string) =>\n input.startsWith('+')\n ? true\n : 'Include country code (e.g., +1234567890)',\n },\n ]);\n\n saveToEnvFile({\n TWILIO_WHATSAPP_FROM: whatsappFrom,\n TWILIO_WHATSAPP_TO: whatsappTo,\n TWILIO_CHANNEL: 'whatsapp',\n });\n } else {\n console.log(chalk.cyan('\\nSMS Setup:'));\n console.log(\n chalk.yellow(' Note: US carriers require A2P 10DLC registration')\n );\n console.log(\n ' Register at: https://console.twilio.com/us1/develop/sms/settings/compliance\\n'\n );\n\n const { smsFrom, smsTo } = await inquirer.prompt([\n {\n type: 'input',\n name: 'smsFrom',\n message: 'Twilio SMS number:',\n default: config.smsFromNumber,\n },\n {\n type: 'input',\n name: 'smsTo',\n message: 'Your phone number:',\n default: config.smsToNumber,\n validate: (input: string) =>\n input.startsWith('+')\n ? true\n : 'Include country code (e.g., +1234567890)',\n },\n ]);\n\n saveToEnvFile({\n TWILIO_SMS_FROM: smsFrom,\n TWILIO_SMS_TO: smsTo,\n TWILIO_CHANNEL: 'sms',\n });\n }\n\n config.enabled = true;\n saveSMSConfig(config);\n\n console.log(chalk.green('\\nNotifications configured!'));\n console.log(chalk.gray('Test with: stackmemory notify test'));\n}\n\nfunction saveToEnvFile(vars: Record<string, string>): void {\n const envDir = join(homedir(), '.stackmemory');\n const envPath = join(envDir, '.env');\n\n if (!existsSync(envDir)) {\n mkdirSync(envDir, { recursive: true });\n }\n\n let content = '';\n if (existsSync(envPath)) {\n content = readFileSync(envPath, 'utf8');\n }\n\n for (const [key, value] of Object.entries(vars)) {\n const regex = new RegExp(`^${key}=.*$`, 'm');\n const line = `${key}=\"${value}\"`;\n\n if (regex.test(content)) {\n content = content.replace(regex, line);\n } else {\n content += `${content.endsWith('\\n') || content === '' ? '' : '\\n'}${line}\\n`;\n }\n }\n\n writeFileSync(envPath, content);\n}\n\nexport default createSettingsCommand;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -6,6 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
8
|
import { join } from "path";
|
|
9
|
+
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
9
10
|
import {
|
|
10
11
|
loadSMSConfig,
|
|
11
12
|
saveSMSConfig,
|
|
@@ -317,6 +318,32 @@ Examples:
|
|
|
317
318
|
)
|
|
318
319
|
);
|
|
319
320
|
});
|
|
321
|
+
cmd.command("check").description(
|
|
322
|
+
"Check for new SMS/WhatsApp responses (use in Claude sessions)"
|
|
323
|
+
).action(() => {
|
|
324
|
+
const responsePath = join(
|
|
325
|
+
process.env["HOME"] || "~",
|
|
326
|
+
".stackmemory",
|
|
327
|
+
"sms-latest-response.json"
|
|
328
|
+
);
|
|
329
|
+
try {
|
|
330
|
+
if (existsSync(responsePath)) {
|
|
331
|
+
const data = JSON.parse(readFileSync(responsePath, "utf8"));
|
|
332
|
+
const age = Date.now() - new Date(data.timestamp).getTime();
|
|
333
|
+
if (age < 5 * 60 * 1e3) {
|
|
334
|
+
console.log(chalk.green.bold("\n*** NEW SMS RESPONSE ***"));
|
|
335
|
+
console.log(` Response: "${data.response}"`);
|
|
336
|
+
console.log(` Prompt ID: ${data.promptId}`);
|
|
337
|
+
console.log(` Received: ${Math.round(age / 1e3)}s ago
|
|
338
|
+
`);
|
|
339
|
+
unlinkSync(responsePath);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
console.log(chalk.gray("No new responses"));
|
|
346
|
+
});
|
|
320
347
|
cmd.command("pending").description("List pending prompts awaiting response").action(() => {
|
|
321
348
|
const config = loadSMSConfig();
|
|
322
349
|
if (config.pendingPrompts.length === 0) {
|
|
@@ -393,6 +420,11 @@ Examples:
|
|
|
393
420
|
const removed = cleanupOldActions();
|
|
394
421
|
console.log(chalk.green(`Removed ${removed} old action(s)`));
|
|
395
422
|
});
|
|
423
|
+
cmd.command("watch-responses").description("Watch for incoming SMS/WhatsApp responses and notify").option("-i, --interval <ms>", "Check interval in milliseconds", "2000").action(async (options) => {
|
|
424
|
+
const { startResponseWatcher } = await import("../../hooks/sms-watcher.js");
|
|
425
|
+
const interval = parseInt(options.interval, 10);
|
|
426
|
+
startResponseWatcher(interval);
|
|
427
|
+
});
|
|
396
428
|
cmd.command("install-hook").description("Install Claude Code notification hook").action(() => {
|
|
397
429
|
try {
|
|
398
430
|
const scriptPath = join(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/commands/sms-notify.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * CLI command for SMS notification management\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { execSync } from 'child_process';\nimport { join } from 'path';\nimport {\n loadSMSConfig,\n saveSMSConfig,\n sendNotification,\n sendSMSNotification,\n notifyReviewReady,\n notifyWithYesNo,\n notifyTaskComplete,\n cleanupExpiredPrompts,\n type MessageChannel,\n} from '../../hooks/sms-notify.js';\nimport {\n loadActionQueue,\n processAllPendingActions,\n cleanupOldActions,\n startActionWatcher,\n} from '../../hooks/sms-action-runner.js';\n\n// __dirname provided by esbuild banner\n\nexport function createSMSNotifyCommand(): Command {\n const cmd = new Command('notify')\n .description(\n 'SMS notification system for review alerts (optional, requires Twilio)'\n )\n .addHelpText(\n 'after',\n `\nSetup (optional):\n 1. Create Twilio account at https://twilio.com\n 2. Get Account SID, Auth Token, and phone numbers\n 3. Set environment variables:\n export TWILIO_ACCOUNT_SID=your_sid\n export TWILIO_AUTH_TOKEN=your_token\n\n For WhatsApp (recommended - cheaper for conversations):\n export TWILIO_WHATSAPP_FROM=+1234567890\n export TWILIO_WHATSAPP_TO=+1234567890\n export TWILIO_CHANNEL=whatsapp\n\n For SMS:\n export TWILIO_SMS_FROM=+1234567890\n export TWILIO_SMS_TO=+1234567890\n export TWILIO_CHANNEL=sms\n\n Legacy (works for both, defaults to WhatsApp):\n export TWILIO_FROM_NUMBER=+1234567890\n export TWILIO_TO_NUMBER=+1234567890\n\n 4. Enable: stackmemory notify enable\n\nExamples:\n stackmemory notify status Check configuration\n stackmemory notify enable Enable notifications\n stackmemory notify channel whatsapp Switch to WhatsApp\n stackmemory notify channel sms Switch to SMS\n stackmemory notify test Send test message\n stackmemory notify send \"PR ready\" Send custom message\n stackmemory notify review \"PR #123\" Send review notification with options\n stackmemory notify ask \"Deploy?\" Send yes/no prompt\n`\n );\n\n cmd\n .command('status')\n .description('Show notification configuration status')\n .action(() => {\n const config = loadSMSConfig();\n\n console.log(chalk.blue('Notification Status:'));\n console.log();\n\n // Check credentials\n const hasCreds = config.accountSid && config.authToken;\n\n // Check channel-specific numbers\n const channel = config.channel || 'whatsapp';\n const hasWhatsApp =\n config.whatsappFromNumber ||\n config.fromNumber ||\n config.whatsappToNumber ||\n config.toNumber;\n const hasSMS =\n config.smsFromNumber ||\n config.fromNumber ||\n config.smsToNumber ||\n config.toNumber;\n const hasNumbers = channel === 'whatsapp' ? hasWhatsApp : hasSMS;\n\n console.log(\n ` ${chalk.gray('Enabled:')} ${config.enabled ? chalk.green('yes') : chalk.red('no')}`\n );\n console.log(\n ` ${chalk.gray('Channel:')} ${channel === 'whatsapp' ? chalk.cyan('WhatsApp') : chalk.blue('SMS')}`\n );\n console.log(\n ` ${chalk.gray('Configured:')} ${hasCreds && hasNumbers ? chalk.green('yes') : chalk.yellow('no (set env vars)')}`\n );\n\n // Show channel-specific numbers\n console.log();\n console.log(chalk.blue('Numbers:'));\n if (channel === 'whatsapp') {\n const from = config.whatsappFromNumber || config.fromNumber;\n const to = config.whatsappToNumber || config.toNumber;\n if (from) {\n console.log(` ${chalk.gray('WhatsApp From:')} ${maskPhone(from)}`);\n }\n if (to) {\n console.log(` ${chalk.gray('WhatsApp To:')} ${maskPhone(to)}`);\n }\n } else {\n const from = config.smsFromNumber || config.fromNumber;\n const to = config.smsToNumber || config.toNumber;\n if (from) {\n console.log(` ${chalk.gray('SMS From:')} ${maskPhone(from)}`);\n }\n if (to) {\n console.log(` ${chalk.gray('SMS To:')} ${maskPhone(to)}`);\n }\n }\n\n console.log();\n console.log(chalk.blue('Notify On:'));\n console.log(\n ` ${chalk.gray('Task Complete:')} ${config.notifyOn.taskComplete ? 'yes' : 'no'}`\n );\n console.log(\n ` ${chalk.gray('Review Ready:')} ${config.notifyOn.reviewReady ? 'yes' : 'no'}`\n );\n console.log(\n ` ${chalk.gray('Errors:')} ${config.notifyOn.error ? 'yes' : 'no'}`\n );\n\n if (config.quietHours?.enabled) {\n console.log();\n console.log(\n chalk.blue(\n `Quiet Hours: ${config.quietHours.start} - ${config.quietHours.end}`\n )\n );\n }\n\n console.log();\n console.log(\n ` ${chalk.gray('Pending Prompts:')} ${config.pendingPrompts.length}`\n );\n console.log(\n ` ${chalk.gray('Response Timeout:')} ${config.responseTimeout}s`\n );\n\n if (!hasCreds || !hasNumbers) {\n console.log();\n console.log(\n chalk.yellow('To configure, set these environment variables:')\n );\n console.log(chalk.gray(' export TWILIO_ACCOUNT_SID=your_sid'));\n console.log(chalk.gray(' export TWILIO_AUTH_TOKEN=your_token'));\n console.log();\n console.log(chalk.gray(' For WhatsApp (recommended):'));\n console.log(chalk.gray(' export TWILIO_WHATSAPP_FROM=+1234567890'));\n console.log(chalk.gray(' export TWILIO_WHATSAPP_TO=+1234567890'));\n console.log();\n console.log(chalk.gray(' For SMS:'));\n console.log(chalk.gray(' export TWILIO_SMS_FROM=+1234567890'));\n console.log(chalk.gray(' export TWILIO_SMS_TO=+1234567890'));\n }\n });\n\n cmd\n .command('enable')\n .description('Enable SMS notifications')\n .action(() => {\n const config = loadSMSConfig();\n config.enabled = true;\n saveSMSConfig(config);\n console.log(chalk.green('SMS notifications enabled'));\n\n const hasCreds =\n config.accountSid &&\n config.authToken &&\n config.fromNumber &&\n config.toNumber;\n if (!hasCreds) {\n console.log(\n chalk.yellow(\n 'Note: Set Twilio environment variables to send messages'\n )\n );\n }\n });\n\n cmd\n .command('disable')\n .description('Disable SMS notifications')\n .action(() => {\n const config = loadSMSConfig();\n config.enabled = false;\n saveSMSConfig(config);\n console.log(chalk.yellow('SMS notifications disabled'));\n });\n\n cmd\n .command('channel <type>')\n .description('Set notification channel (whatsapp|sms)')\n .action((type: string) => {\n const validChannels: MessageChannel[] = ['whatsapp', 'sms'];\n const channel = type.toLowerCase() as MessageChannel;\n\n if (!validChannels.includes(channel)) {\n console.log(\n chalk.red(`Invalid channel. Use: ${validChannels.join(', ')}`)\n );\n return;\n }\n\n const config = loadSMSConfig();\n config.channel = channel;\n saveSMSConfig(config);\n\n const label = channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n console.log(chalk.green(`Notification channel set to ${label}`));\n\n // Show relevant env vars\n if (channel === 'whatsapp') {\n const hasNumbers = config.whatsappFromNumber || config.fromNumber;\n if (!hasNumbers) {\n console.log(\n chalk.yellow('Set TWILIO_WHATSAPP_FROM and TWILIO_WHATSAPP_TO')\n );\n }\n } else {\n const hasNumbers = config.smsFromNumber || config.fromNumber;\n if (!hasNumbers) {\n console.log(chalk.yellow('Set TWILIO_SMS_FROM and TWILIO_SMS_TO'));\n }\n }\n });\n\n cmd\n .command('test')\n .description('Send a test notification')\n .option('--sms', 'Force SMS channel')\n .option('--whatsapp', 'Force WhatsApp channel')\n .action(async (options: { sms?: boolean; whatsapp?: boolean }) => {\n const config = loadSMSConfig();\n const channelOverride: MessageChannel | undefined = options.sms\n ? 'sms'\n : options.whatsapp\n ? 'whatsapp'\n : undefined;\n const channelLabel =\n channelOverride || config.channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n\n console.log(\n chalk.blue(`Sending test notification via ${channelLabel}...`)\n );\n\n const result = await sendNotification(\n {\n type: 'custom',\n title: 'StackMemory Test',\n message: 'This is a test notification from StackMemory.',\n },\n channelOverride\n );\n\n if (result.success) {\n const usedChannel = result.channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n console.log(chalk.green(`Test message sent via ${usedChannel}!`));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('send <message>')\n .description('Send a custom notification')\n .option('-t, --title <title>', 'Message title', 'StackMemory Alert')\n .action(async (message: string, options: { title: string }) => {\n const result = await sendSMSNotification({\n type: 'custom',\n title: options.title,\n message,\n });\n\n if (result.success) {\n console.log(chalk.green('Message sent!'));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('review <title>')\n .description('Send review-ready notification with options')\n .option('-m, --message <msg>', 'Description', 'Ready for your review')\n .option(\n '-o, --options <opts>',\n 'Comma-separated options',\n 'Approve,Request Changes,Skip'\n )\n .action(\n async (title: string, options: { message: string; options: string }) => {\n const opts = options.options.split(',').map((o) => ({\n label: o.trim(),\n }));\n\n console.log(chalk.blue('Sending review notification...'));\n\n const result = await notifyReviewReady(title, options.message, opts);\n\n if (result.success) {\n console.log(chalk.green('Review notification sent!'));\n if (result.promptId) {\n console.log(chalk.gray(`Prompt ID: ${result.promptId}`));\n }\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n }\n );\n\n cmd\n .command('ask <question>')\n .description('Send a yes/no prompt')\n .option('-t, --title <title>', 'Message title', 'StackMemory')\n .action(async (question: string, options: { title: string }) => {\n console.log(chalk.blue('Sending yes/no prompt...'));\n\n const result = await notifyWithYesNo(options.title, question);\n\n if (result.success) {\n console.log(chalk.green('Prompt sent!'));\n if (result.promptId) {\n console.log(chalk.gray(`Prompt ID: ${result.promptId}`));\n }\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('complete <task>')\n .description('Send task completion notification')\n .option('-s, --summary <text>', 'Task summary', '')\n .action(async (task: string, options: { summary: string }) => {\n const result = await notifyTaskComplete(\n task,\n options.summary || `Task \"${task}\" has been completed.`\n );\n\n if (result.success) {\n console.log(chalk.green('Completion notification sent!'));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('quiet')\n .description('Configure quiet hours')\n .option('--enable', 'Enable quiet hours')\n .option('--disable', 'Disable quiet hours')\n .option('--start <time>', 'Start time (HH:MM)', '22:00')\n .option('--end <time>', 'End time (HH:MM)', '08:00')\n .action(\n (options: {\n enable?: boolean;\n disable?: boolean;\n start: string;\n end: string;\n }) => {\n const config = loadSMSConfig();\n\n if (!config.quietHours) {\n config.quietHours = { enabled: false, start: '22:00', end: '08:00' };\n }\n\n if (options.enable) {\n config.quietHours.enabled = true;\n } else if (options.disable) {\n config.quietHours.enabled = false;\n }\n\n if (options.start) {\n config.quietHours.start = options.start;\n }\n if (options.end) {\n config.quietHours.end = options.end;\n }\n\n saveSMSConfig(config);\n\n if (config.quietHours.enabled) {\n console.log(\n chalk.green(\n `Quiet hours enabled: ${config.quietHours.start} - ${config.quietHours.end}`\n )\n );\n } else {\n console.log(chalk.yellow('Quiet hours disabled'));\n }\n }\n );\n\n cmd\n .command('toggle <type>')\n .description(\n 'Toggle notification type (taskComplete|reviewReady|error|custom)'\n )\n .action((type: string) => {\n const config = loadSMSConfig();\n const validTypes = ['taskComplete', 'reviewReady', 'error', 'custom'];\n\n if (!validTypes.includes(type)) {\n console.log(chalk.red(`Invalid type. Use: ${validTypes.join(', ')}`));\n return;\n }\n\n const key = type as keyof typeof config.notifyOn;\n config.notifyOn[key] = !config.notifyOn[key];\n saveSMSConfig(config);\n\n console.log(\n chalk.green(\n `${type} notifications ${config.notifyOn[key] ? 'enabled' : 'disabled'}`\n )\n );\n });\n\n cmd\n .command('pending')\n .description('List pending prompts awaiting response')\n .action(() => {\n const config = loadSMSConfig();\n\n if (config.pendingPrompts.length === 0) {\n console.log(chalk.gray('No pending prompts'));\n return;\n }\n\n console.log(chalk.blue('Pending Prompts:'));\n config.pendingPrompts.forEach((p) => {\n const expires = new Date(p.expiresAt);\n const remaining = Math.round((expires.getTime() - Date.now()) / 1000);\n\n console.log();\n console.log(` ${chalk.gray('ID:')} ${p.id}`);\n console.log(` ${chalk.gray('Type:')} ${p.type}`);\n console.log(\n ` ${chalk.gray('Message:')} ${p.message.substring(0, 50)}...`\n );\n console.log(\n ` ${chalk.gray('Expires:')} ${remaining > 0 ? `${remaining}s` : chalk.red('expired')}`\n );\n });\n });\n\n cmd\n .command('cleanup')\n .description('Remove expired pending prompts')\n .action(() => {\n const removed = cleanupExpiredPrompts();\n console.log(chalk.green(`Removed ${removed} expired prompt(s)`));\n });\n\n cmd\n .command('timeout <seconds>')\n .description('Set response timeout for prompts')\n .action((seconds: string) => {\n const config = loadSMSConfig();\n const timeout = parseInt(seconds, 10);\n\n if (isNaN(timeout) || timeout < 30) {\n console.log(chalk.red('Timeout must be at least 30 seconds'));\n return;\n }\n\n config.responseTimeout = timeout;\n saveSMSConfig(config);\n console.log(chalk.green(`Response timeout set to ${timeout} seconds`));\n });\n\n // Action queue commands\n cmd\n .command('actions')\n .description('List queued actions from SMS responses')\n .action(() => {\n const queue = loadActionQueue();\n\n if (queue.actions.length === 0) {\n console.log(chalk.gray('No actions in queue'));\n return;\n }\n\n console.log(chalk.blue('Action Queue:'));\n queue.actions.forEach((a) => {\n const statusColor =\n a.status === 'completed'\n ? chalk.green\n : a.status === 'failed'\n ? chalk.red\n : a.status === 'running'\n ? chalk.yellow\n : chalk.gray;\n\n console.log();\n console.log(` ${chalk.gray('ID:')} ${a.id}`);\n console.log(` ${chalk.gray('Status:')} ${statusColor(a.status)}`);\n console.log(\n ` ${chalk.gray('Action:')} ${a.action.substring(0, 60)}...`\n );\n console.log(` ${chalk.gray('Response:')} ${a.response}`);\n if (a.error) {\n console.log(` ${chalk.gray('Error:')} ${chalk.red(a.error)}`);\n }\n });\n });\n\n cmd\n .command('run-actions')\n .description('Execute all pending actions from SMS responses')\n .action(() => {\n console.log(chalk.blue('Processing pending actions...'));\n const result = processAllPendingActions();\n\n console.log(\n chalk.green(\n `Processed ${result.processed} action(s): ${result.succeeded} succeeded, ${result.failed} failed`\n )\n );\n });\n\n cmd\n .command('watch')\n .description('Watch for and execute SMS response actions')\n .option('-i, --interval <ms>', 'Check interval in milliseconds', '5000')\n .action((options: { interval: string }) => {\n const interval = parseInt(options.interval, 10);\n console.log(chalk.blue(`Watching for actions (interval: ${interval}ms)`));\n console.log(chalk.gray('Press Ctrl+C to stop'));\n\n startActionWatcher(interval);\n });\n\n cmd\n .command('cleanup-actions')\n .description('Remove old completed actions')\n .action(() => {\n const removed = cleanupOldActions();\n console.log(chalk.green(`Removed ${removed} old action(s)`));\n });\n\n // Hook installation commands\n cmd\n .command('install-hook')\n .description('Install Claude Code notification hook')\n .action(() => {\n try {\n const scriptPath = join(\n __dirname,\n '../../../scripts/install-notify-hook.sh'\n );\n execSync(`bash \"${scriptPath}\"`, { stdio: 'inherit' });\n } catch {\n console.error(chalk.red('Failed to install hook'));\n }\n });\n\n cmd\n .command('install-response-hook')\n .description('Install Claude Code response handler hook')\n .action(() => {\n try {\n // Create install script inline\n const hooksDir = join(process.env['HOME'] || '~', '.claude', 'hooks');\n const hookSrc = join(\n __dirname,\n '../../../templates/claude-hooks/sms-response-handler.js'\n );\n const hookDest = join(hooksDir, 'sms-response-handler.js');\n\n execSync(`mkdir -p \"${hooksDir}\"`, { stdio: 'inherit' });\n execSync(`cp \"${hookSrc}\" \"${hookDest}\"`, { stdio: 'inherit' });\n execSync(`chmod +x \"${hookDest}\"`, { stdio: 'inherit' });\n\n console.log(chalk.green('Response handler hook installed!'));\n console.log(chalk.gray(`Location: ${hookDest}`));\n console.log();\n console.log(chalk.blue('Add to ~/.claude/settings.json:'));\n console.log(\n chalk.gray(` \"hooks\": { \"pre_tool_use\": [\"node ${hookDest}\"] }`)\n );\n } catch {\n console.error(chalk.red('Failed to install response hook'));\n }\n });\n\n cmd\n .command('webhook')\n .description('Start SMS webhook server for receiving responses')\n .option('-p, --port <port>', 'Port to listen on', '3456')\n .action(async (options: { port: string }) => {\n const { startWebhookServer } = await import('../../hooks/sms-webhook.js');\n const port = parseInt(options.port, 10);\n startWebhookServer(port);\n });\n\n return cmd;\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length < 8) return phone;\n return phone.substring(0, 4) + '****' + phone.substring(phone.length - 2);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * CLI command for SMS notification management\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { execSync } from 'child_process';\nimport { join } from 'path';\nimport { existsSync, readFileSync, unlinkSync } from 'fs';\nimport {\n loadSMSConfig,\n saveSMSConfig,\n sendNotification,\n sendSMSNotification,\n notifyReviewReady,\n notifyWithYesNo,\n notifyTaskComplete,\n cleanupExpiredPrompts,\n type MessageChannel,\n} from '../../hooks/sms-notify.js';\nimport {\n loadActionQueue,\n processAllPendingActions,\n cleanupOldActions,\n startActionWatcher,\n} from '../../hooks/sms-action-runner.js';\n\n// __dirname provided by esbuild banner\n\nexport function createSMSNotifyCommand(): Command {\n const cmd = new Command('notify')\n .description(\n 'SMS notification system for review alerts (optional, requires Twilio)'\n )\n .addHelpText(\n 'after',\n `\nSetup (optional):\n 1. Create Twilio account at https://twilio.com\n 2. Get Account SID, Auth Token, and phone numbers\n 3. Set environment variables:\n export TWILIO_ACCOUNT_SID=your_sid\n export TWILIO_AUTH_TOKEN=your_token\n\n For WhatsApp (recommended - cheaper for conversations):\n export TWILIO_WHATSAPP_FROM=+1234567890\n export TWILIO_WHATSAPP_TO=+1234567890\n export TWILIO_CHANNEL=whatsapp\n\n For SMS:\n export TWILIO_SMS_FROM=+1234567890\n export TWILIO_SMS_TO=+1234567890\n export TWILIO_CHANNEL=sms\n\n Legacy (works for both, defaults to WhatsApp):\n export TWILIO_FROM_NUMBER=+1234567890\n export TWILIO_TO_NUMBER=+1234567890\n\n 4. Enable: stackmemory notify enable\n\nExamples:\n stackmemory notify status Check configuration\n stackmemory notify enable Enable notifications\n stackmemory notify channel whatsapp Switch to WhatsApp\n stackmemory notify channel sms Switch to SMS\n stackmemory notify test Send test message\n stackmemory notify send \"PR ready\" Send custom message\n stackmemory notify review \"PR #123\" Send review notification with options\n stackmemory notify ask \"Deploy?\" Send yes/no prompt\n`\n );\n\n cmd\n .command('status')\n .description('Show notification configuration status')\n .action(() => {\n const config = loadSMSConfig();\n\n console.log(chalk.blue('Notification Status:'));\n console.log();\n\n // Check credentials\n const hasCreds = config.accountSid && config.authToken;\n\n // Check channel-specific numbers\n const channel = config.channel || 'whatsapp';\n const hasWhatsApp =\n config.whatsappFromNumber ||\n config.fromNumber ||\n config.whatsappToNumber ||\n config.toNumber;\n const hasSMS =\n config.smsFromNumber ||\n config.fromNumber ||\n config.smsToNumber ||\n config.toNumber;\n const hasNumbers = channel === 'whatsapp' ? hasWhatsApp : hasSMS;\n\n console.log(\n ` ${chalk.gray('Enabled:')} ${config.enabled ? chalk.green('yes') : chalk.red('no')}`\n );\n console.log(\n ` ${chalk.gray('Channel:')} ${channel === 'whatsapp' ? chalk.cyan('WhatsApp') : chalk.blue('SMS')}`\n );\n console.log(\n ` ${chalk.gray('Configured:')} ${hasCreds && hasNumbers ? chalk.green('yes') : chalk.yellow('no (set env vars)')}`\n );\n\n // Show channel-specific numbers\n console.log();\n console.log(chalk.blue('Numbers:'));\n if (channel === 'whatsapp') {\n const from = config.whatsappFromNumber || config.fromNumber;\n const to = config.whatsappToNumber || config.toNumber;\n if (from) {\n console.log(` ${chalk.gray('WhatsApp From:')} ${maskPhone(from)}`);\n }\n if (to) {\n console.log(` ${chalk.gray('WhatsApp To:')} ${maskPhone(to)}`);\n }\n } else {\n const from = config.smsFromNumber || config.fromNumber;\n const to = config.smsToNumber || config.toNumber;\n if (from) {\n console.log(` ${chalk.gray('SMS From:')} ${maskPhone(from)}`);\n }\n if (to) {\n console.log(` ${chalk.gray('SMS To:')} ${maskPhone(to)}`);\n }\n }\n\n console.log();\n console.log(chalk.blue('Notify On:'));\n console.log(\n ` ${chalk.gray('Task Complete:')} ${config.notifyOn.taskComplete ? 'yes' : 'no'}`\n );\n console.log(\n ` ${chalk.gray('Review Ready:')} ${config.notifyOn.reviewReady ? 'yes' : 'no'}`\n );\n console.log(\n ` ${chalk.gray('Errors:')} ${config.notifyOn.error ? 'yes' : 'no'}`\n );\n\n if (config.quietHours?.enabled) {\n console.log();\n console.log(\n chalk.blue(\n `Quiet Hours: ${config.quietHours.start} - ${config.quietHours.end}`\n )\n );\n }\n\n console.log();\n console.log(\n ` ${chalk.gray('Pending Prompts:')} ${config.pendingPrompts.length}`\n );\n console.log(\n ` ${chalk.gray('Response Timeout:')} ${config.responseTimeout}s`\n );\n\n if (!hasCreds || !hasNumbers) {\n console.log();\n console.log(\n chalk.yellow('To configure, set these environment variables:')\n );\n console.log(chalk.gray(' export TWILIO_ACCOUNT_SID=your_sid'));\n console.log(chalk.gray(' export TWILIO_AUTH_TOKEN=your_token'));\n console.log();\n console.log(chalk.gray(' For WhatsApp (recommended):'));\n console.log(chalk.gray(' export TWILIO_WHATSAPP_FROM=+1234567890'));\n console.log(chalk.gray(' export TWILIO_WHATSAPP_TO=+1234567890'));\n console.log();\n console.log(chalk.gray(' For SMS:'));\n console.log(chalk.gray(' export TWILIO_SMS_FROM=+1234567890'));\n console.log(chalk.gray(' export TWILIO_SMS_TO=+1234567890'));\n }\n });\n\n cmd\n .command('enable')\n .description('Enable SMS notifications')\n .action(() => {\n const config = loadSMSConfig();\n config.enabled = true;\n saveSMSConfig(config);\n console.log(chalk.green('SMS notifications enabled'));\n\n const hasCreds =\n config.accountSid &&\n config.authToken &&\n config.fromNumber &&\n config.toNumber;\n if (!hasCreds) {\n console.log(\n chalk.yellow(\n 'Note: Set Twilio environment variables to send messages'\n )\n );\n }\n });\n\n cmd\n .command('disable')\n .description('Disable SMS notifications')\n .action(() => {\n const config = loadSMSConfig();\n config.enabled = false;\n saveSMSConfig(config);\n console.log(chalk.yellow('SMS notifications disabled'));\n });\n\n cmd\n .command('channel <type>')\n .description('Set notification channel (whatsapp|sms)')\n .action((type: string) => {\n const validChannels: MessageChannel[] = ['whatsapp', 'sms'];\n const channel = type.toLowerCase() as MessageChannel;\n\n if (!validChannels.includes(channel)) {\n console.log(\n chalk.red(`Invalid channel. Use: ${validChannels.join(', ')}`)\n );\n return;\n }\n\n const config = loadSMSConfig();\n config.channel = channel;\n saveSMSConfig(config);\n\n const label = channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n console.log(chalk.green(`Notification channel set to ${label}`));\n\n // Show relevant env vars\n if (channel === 'whatsapp') {\n const hasNumbers = config.whatsappFromNumber || config.fromNumber;\n if (!hasNumbers) {\n console.log(\n chalk.yellow('Set TWILIO_WHATSAPP_FROM and TWILIO_WHATSAPP_TO')\n );\n }\n } else {\n const hasNumbers = config.smsFromNumber || config.fromNumber;\n if (!hasNumbers) {\n console.log(chalk.yellow('Set TWILIO_SMS_FROM and TWILIO_SMS_TO'));\n }\n }\n });\n\n cmd\n .command('test')\n .description('Send a test notification')\n .option('--sms', 'Force SMS channel')\n .option('--whatsapp', 'Force WhatsApp channel')\n .action(async (options: { sms?: boolean; whatsapp?: boolean }) => {\n const config = loadSMSConfig();\n const channelOverride: MessageChannel | undefined = options.sms\n ? 'sms'\n : options.whatsapp\n ? 'whatsapp'\n : undefined;\n const channelLabel =\n channelOverride || config.channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n\n console.log(\n chalk.blue(`Sending test notification via ${channelLabel}...`)\n );\n\n const result = await sendNotification(\n {\n type: 'custom',\n title: 'StackMemory Test',\n message: 'This is a test notification from StackMemory.',\n },\n channelOverride\n );\n\n if (result.success) {\n const usedChannel = result.channel === 'whatsapp' ? 'WhatsApp' : 'SMS';\n console.log(chalk.green(`Test message sent via ${usedChannel}!`));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('send <message>')\n .description('Send a custom notification')\n .option('-t, --title <title>', 'Message title', 'StackMemory Alert')\n .action(async (message: string, options: { title: string }) => {\n const result = await sendSMSNotification({\n type: 'custom',\n title: options.title,\n message,\n });\n\n if (result.success) {\n console.log(chalk.green('Message sent!'));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('review <title>')\n .description('Send review-ready notification with options')\n .option('-m, --message <msg>', 'Description', 'Ready for your review')\n .option(\n '-o, --options <opts>',\n 'Comma-separated options',\n 'Approve,Request Changes,Skip'\n )\n .action(\n async (title: string, options: { message: string; options: string }) => {\n const opts = options.options.split(',').map((o) => ({\n label: o.trim(),\n }));\n\n console.log(chalk.blue('Sending review notification...'));\n\n const result = await notifyReviewReady(title, options.message, opts);\n\n if (result.success) {\n console.log(chalk.green('Review notification sent!'));\n if (result.promptId) {\n console.log(chalk.gray(`Prompt ID: ${result.promptId}`));\n }\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n }\n );\n\n cmd\n .command('ask <question>')\n .description('Send a yes/no prompt')\n .option('-t, --title <title>', 'Message title', 'StackMemory')\n .action(async (question: string, options: { title: string }) => {\n console.log(chalk.blue('Sending yes/no prompt...'));\n\n const result = await notifyWithYesNo(options.title, question);\n\n if (result.success) {\n console.log(chalk.green('Prompt sent!'));\n if (result.promptId) {\n console.log(chalk.gray(`Prompt ID: ${result.promptId}`));\n }\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('complete <task>')\n .description('Send task completion notification')\n .option('-s, --summary <text>', 'Task summary', '')\n .action(async (task: string, options: { summary: string }) => {\n const result = await notifyTaskComplete(\n task,\n options.summary || `Task \"${task}\" has been completed.`\n );\n\n if (result.success) {\n console.log(chalk.green('Completion notification sent!'));\n } else {\n console.log(chalk.red(`Failed: ${result.error}`));\n }\n });\n\n cmd\n .command('quiet')\n .description('Configure quiet hours')\n .option('--enable', 'Enable quiet hours')\n .option('--disable', 'Disable quiet hours')\n .option('--start <time>', 'Start time (HH:MM)', '22:00')\n .option('--end <time>', 'End time (HH:MM)', '08:00')\n .action(\n (options: {\n enable?: boolean;\n disable?: boolean;\n start: string;\n end: string;\n }) => {\n const config = loadSMSConfig();\n\n if (!config.quietHours) {\n config.quietHours = { enabled: false, start: '22:00', end: '08:00' };\n }\n\n if (options.enable) {\n config.quietHours.enabled = true;\n } else if (options.disable) {\n config.quietHours.enabled = false;\n }\n\n if (options.start) {\n config.quietHours.start = options.start;\n }\n if (options.end) {\n config.quietHours.end = options.end;\n }\n\n saveSMSConfig(config);\n\n if (config.quietHours.enabled) {\n console.log(\n chalk.green(\n `Quiet hours enabled: ${config.quietHours.start} - ${config.quietHours.end}`\n )\n );\n } else {\n console.log(chalk.yellow('Quiet hours disabled'));\n }\n }\n );\n\n cmd\n .command('toggle <type>')\n .description(\n 'Toggle notification type (taskComplete|reviewReady|error|custom)'\n )\n .action((type: string) => {\n const config = loadSMSConfig();\n const validTypes = ['taskComplete', 'reviewReady', 'error', 'custom'];\n\n if (!validTypes.includes(type)) {\n console.log(chalk.red(`Invalid type. Use: ${validTypes.join(', ')}`));\n return;\n }\n\n const key = type as keyof typeof config.notifyOn;\n config.notifyOn[key] = !config.notifyOn[key];\n saveSMSConfig(config);\n\n console.log(\n chalk.green(\n `${type} notifications ${config.notifyOn[key] ? 'enabled' : 'disabled'}`\n )\n );\n });\n\n cmd\n .command('check')\n .description(\n 'Check for new SMS/WhatsApp responses (use in Claude sessions)'\n )\n .action(() => {\n const responsePath = join(\n process.env['HOME'] || '~',\n '.stackmemory',\n 'sms-latest-response.json'\n );\n\n try {\n if (existsSync(responsePath)) {\n const data = JSON.parse(readFileSync(responsePath, 'utf8'));\n const age = Date.now() - new Date(data.timestamp).getTime();\n\n if (age < 5 * 60 * 1000) {\n // Less than 5 minutes old\n console.log(chalk.green.bold('\\n*** NEW SMS RESPONSE ***'));\n console.log(` Response: \"${data.response}\"`);\n console.log(` Prompt ID: ${data.promptId}`);\n console.log(` Received: ${Math.round(age / 1000)}s ago\\n`);\n\n // Clear it after reading\n unlinkSync(responsePath);\n return;\n }\n }\n } catch {\n // Ignore errors\n }\n\n console.log(chalk.gray('No new responses'));\n });\n\n cmd\n .command('pending')\n .description('List pending prompts awaiting response')\n .action(() => {\n const config = loadSMSConfig();\n\n if (config.pendingPrompts.length === 0) {\n console.log(chalk.gray('No pending prompts'));\n return;\n }\n\n console.log(chalk.blue('Pending Prompts:'));\n config.pendingPrompts.forEach((p) => {\n const expires = new Date(p.expiresAt);\n const remaining = Math.round((expires.getTime() - Date.now()) / 1000);\n\n console.log();\n console.log(` ${chalk.gray('ID:')} ${p.id}`);\n console.log(` ${chalk.gray('Type:')} ${p.type}`);\n console.log(\n ` ${chalk.gray('Message:')} ${p.message.substring(0, 50)}...`\n );\n console.log(\n ` ${chalk.gray('Expires:')} ${remaining > 0 ? `${remaining}s` : chalk.red('expired')}`\n );\n });\n });\n\n cmd\n .command('cleanup')\n .description('Remove expired pending prompts')\n .action(() => {\n const removed = cleanupExpiredPrompts();\n console.log(chalk.green(`Removed ${removed} expired prompt(s)`));\n });\n\n cmd\n .command('timeout <seconds>')\n .description('Set response timeout for prompts')\n .action((seconds: string) => {\n const config = loadSMSConfig();\n const timeout = parseInt(seconds, 10);\n\n if (isNaN(timeout) || timeout < 30) {\n console.log(chalk.red('Timeout must be at least 30 seconds'));\n return;\n }\n\n config.responseTimeout = timeout;\n saveSMSConfig(config);\n console.log(chalk.green(`Response timeout set to ${timeout} seconds`));\n });\n\n // Action queue commands\n cmd\n .command('actions')\n .description('List queued actions from SMS responses')\n .action(() => {\n const queue = loadActionQueue();\n\n if (queue.actions.length === 0) {\n console.log(chalk.gray('No actions in queue'));\n return;\n }\n\n console.log(chalk.blue('Action Queue:'));\n queue.actions.forEach((a) => {\n const statusColor =\n a.status === 'completed'\n ? chalk.green\n : a.status === 'failed'\n ? chalk.red\n : a.status === 'running'\n ? chalk.yellow\n : chalk.gray;\n\n console.log();\n console.log(` ${chalk.gray('ID:')} ${a.id}`);\n console.log(` ${chalk.gray('Status:')} ${statusColor(a.status)}`);\n console.log(\n ` ${chalk.gray('Action:')} ${a.action.substring(0, 60)}...`\n );\n console.log(` ${chalk.gray('Response:')} ${a.response}`);\n if (a.error) {\n console.log(` ${chalk.gray('Error:')} ${chalk.red(a.error)}`);\n }\n });\n });\n\n cmd\n .command('run-actions')\n .description('Execute all pending actions from SMS responses')\n .action(() => {\n console.log(chalk.blue('Processing pending actions...'));\n const result = processAllPendingActions();\n\n console.log(\n chalk.green(\n `Processed ${result.processed} action(s): ${result.succeeded} succeeded, ${result.failed} failed`\n )\n );\n });\n\n cmd\n .command('watch')\n .description('Watch for and execute SMS response actions')\n .option('-i, --interval <ms>', 'Check interval in milliseconds', '5000')\n .action((options: { interval: string }) => {\n const interval = parseInt(options.interval, 10);\n console.log(chalk.blue(`Watching for actions (interval: ${interval}ms)`));\n console.log(chalk.gray('Press Ctrl+C to stop'));\n\n startActionWatcher(interval);\n });\n\n cmd\n .command('cleanup-actions')\n .description('Remove old completed actions')\n .action(() => {\n const removed = cleanupOldActions();\n console.log(chalk.green(`Removed ${removed} old action(s)`));\n });\n\n cmd\n .command('watch-responses')\n .description('Watch for incoming SMS/WhatsApp responses and notify')\n .option('-i, --interval <ms>', 'Check interval in milliseconds', '2000')\n .action(async (options: { interval: string }) => {\n const { startResponseWatcher } =\n await import('../../hooks/sms-watcher.js');\n const interval = parseInt(options.interval, 10);\n startResponseWatcher(interval);\n });\n\n // Hook installation commands\n cmd\n .command('install-hook')\n .description('Install Claude Code notification hook')\n .action(() => {\n try {\n const scriptPath = join(\n __dirname,\n '../../../scripts/install-notify-hook.sh'\n );\n execSync(`bash \"${scriptPath}\"`, { stdio: 'inherit' });\n } catch {\n console.error(chalk.red('Failed to install hook'));\n }\n });\n\n cmd\n .command('install-response-hook')\n .description('Install Claude Code response handler hook')\n .action(() => {\n try {\n // Create install script inline\n const hooksDir = join(process.env['HOME'] || '~', '.claude', 'hooks');\n const hookSrc = join(\n __dirname,\n '../../../templates/claude-hooks/sms-response-handler.js'\n );\n const hookDest = join(hooksDir, 'sms-response-handler.js');\n\n execSync(`mkdir -p \"${hooksDir}\"`, { stdio: 'inherit' });\n execSync(`cp \"${hookSrc}\" \"${hookDest}\"`, { stdio: 'inherit' });\n execSync(`chmod +x \"${hookDest}\"`, { stdio: 'inherit' });\n\n console.log(chalk.green('Response handler hook installed!'));\n console.log(chalk.gray(`Location: ${hookDest}`));\n console.log();\n console.log(chalk.blue('Add to ~/.claude/settings.json:'));\n console.log(\n chalk.gray(` \"hooks\": { \"pre_tool_use\": [\"node ${hookDest}\"] }`)\n );\n } catch {\n console.error(chalk.red('Failed to install response hook'));\n }\n });\n\n cmd\n .command('webhook')\n .description('Start SMS webhook server for receiving responses')\n .option('-p, --port <port>', 'Port to listen on', '3456')\n .action(async (options: { port: string }) => {\n const { startWebhookServer } = await import('../../hooks/sms-webhook.js');\n const port = parseInt(options.port, 10);\n startWebhookServer(port);\n });\n\n return cmd;\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length < 8) return phone;\n return phone.substring(0, 4) + '****' + phone.substring(phone.length - 2);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -48,6 +48,7 @@ import { createAPICommand } from "./commands/api.js";
|
|
|
48
48
|
import { createCleanupProcessesCommand } from "./commands/cleanup-processes.js";
|
|
49
49
|
import { createAutoBackgroundCommand } from "./commands/auto-background.js";
|
|
50
50
|
import { createSMSNotifyCommand } from "./commands/sms-notify.js";
|
|
51
|
+
import { createSettingsCommand } from "./commands/settings.js";
|
|
51
52
|
import { ProjectManager } from "../core/projects/project-manager.js";
|
|
52
53
|
import Database from "better-sqlite3";
|
|
53
54
|
import { join } from "path";
|
|
@@ -500,6 +501,7 @@ program.addCommand(createAPICommand());
|
|
|
500
501
|
program.addCommand(createCleanupProcessesCommand());
|
|
501
502
|
program.addCommand(createAutoBackgroundCommand());
|
|
502
503
|
program.addCommand(createSMSNotifyCommand());
|
|
504
|
+
program.addCommand(createSettingsCommand());
|
|
503
505
|
program.command("dashboard").description("Display monitoring dashboard in terminal").option("-w, --watch", "Auto-refresh dashboard").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (options) => {
|
|
504
506
|
const { dashboardCommand } = await import("./commands/dashboard.js");
|
|
505
507
|
await dashboardCommand.handler(options);
|