Skip to content

Commit 20b6f91

Browse files
feat(claude-sm): Add remote mode and configurable defaults
- Add --remote/-r flag for WhatsApp notifications on all questions - Add config commands: remote-on, remote-off, set remote - Add --no-worktree/-W to override worktree default - Remove verbose trace config logging (dead code cleanup) - Store defaults in ~/.stackmemory/claude-sm.json WhatsApp notifications now only trigger when: - Remote mode enabled (--remote flag) - OR question has multiple options (task choices)
1 parent 6882348 commit 20b6f91

2 files changed

Lines changed: 178 additions & 70 deletions

File tree

src/cli/claude-sm.ts

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,21 @@ import { initializeTracing, trace } from '../core/trace/index.js';
1616

1717
// __filename and __dirname are provided by esbuild banner for ESM compatibility
1818

19+
interface ClaudeSMConfig {
20+
defaultWorktree: boolean;
21+
defaultSandbox: boolean;
22+
defaultChrome: boolean;
23+
defaultTracing: boolean;
24+
defaultRemote: boolean;
25+
}
26+
1927
interface ClaudeConfig {
2028
instanceId: string;
2129
worktreePath?: string;
2230
useSandbox: boolean;
2331
useChrome: boolean;
2432
useWorktree: boolean;
33+
useRemote: boolean;
2534
contextEnabled: boolean;
2635
branch?: string;
2736
task?: string;
@@ -30,20 +39,59 @@ interface ClaudeConfig {
3039
claudeBin?: string;
3140
}
3241

42+
const DEFAULT_SM_CONFIG: ClaudeSMConfig = {
43+
defaultWorktree: false,
44+
defaultSandbox: false,
45+
defaultChrome: false,
46+
defaultTracing: true,
47+
defaultRemote: false,
48+
};
49+
50+
function getConfigPath(): string {
51+
return path.join(os.homedir(), '.stackmemory', 'claude-sm.json');
52+
}
53+
54+
function loadSMConfig(): ClaudeSMConfig {
55+
try {
56+
const configPath = getConfigPath();
57+
if (fs.existsSync(configPath)) {
58+
const content = fs.readFileSync(configPath, 'utf8');
59+
return { ...DEFAULT_SM_CONFIG, ...JSON.parse(content) };
60+
}
61+
} catch {
62+
// Ignore errors, use defaults
63+
}
64+
return { ...DEFAULT_SM_CONFIG };
65+
}
66+
67+
function saveSMConfig(config: ClaudeSMConfig): void {
68+
const configPath = getConfigPath();
69+
const dir = path.dirname(configPath);
70+
if (!fs.existsSync(dir)) {
71+
fs.mkdirSync(dir, { recursive: true });
72+
}
73+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
74+
}
75+
3376
class ClaudeSM {
3477
private config: ClaudeConfig;
3578
private stackmemoryPath: string;
3679
private worktreeScriptPath: string;
3780
private claudeConfigDir: string;
81+
private smConfig: ClaudeSMConfig;
3882

3983
constructor() {
84+
// Load persistent defaults
85+
this.smConfig = loadSMConfig();
86+
4087
this.config = {
4188
instanceId: this.generateInstanceId(),
42-
useSandbox: false,
43-
useChrome: false,
44-
useWorktree: false,
89+
useSandbox: this.smConfig.defaultSandbox,
90+
useChrome: this.smConfig.defaultChrome,
91+
useWorktree: this.smConfig.defaultWorktree,
92+
useRemote: this.smConfig.defaultRemote,
4593
contextEnabled: true,
46-
tracingEnabled: true, // Enable tracing by default for claude-sm
94+
tracingEnabled: this.smConfig.defaultTracing,
4795
verboseTracing: false,
4896
};
4997

@@ -288,6 +336,17 @@ class ClaudeSM {
288336
case '-w':
289337
this.config.useWorktree = true;
290338
break;
339+
case '--no-worktree':
340+
case '-W':
341+
this.config.useWorktree = false;
342+
break;
343+
case '--remote':
344+
case '-r':
345+
this.config.useRemote = true;
346+
break;
347+
case '--no-remote':
348+
this.config.useRemote = false;
349+
break;
291350
case '--sandbox':
292351
case '-s':
293352
this.config.useSandbox = true;
@@ -413,13 +472,21 @@ class ClaudeSM {
413472
if (this.config.worktreePath) {
414473
process.env['CLAUDE_WORKTREE_PATH'] = this.config.worktreePath;
415474
}
475+
if (this.config.useRemote) {
476+
process.env['CLAUDE_REMOTE'] = '1';
477+
}
416478

417479
console.log(chalk.gray(`🤖 Instance ID: ${this.config.instanceId}`));
418480
console.log(chalk.gray(`📁 Working in: ${process.cwd()}`));
419481

420482
if (this.config.useSandbox) {
421483
console.log(chalk.yellow('🔒 Sandbox mode enabled'));
422484
}
485+
if (this.config.useRemote) {
486+
console.log(
487+
chalk.cyan('📱 Remote mode: WhatsApp notifications for all questions')
488+
);
489+
}
423490
if (this.config.useChrome) {
424491
console.log(chalk.yellow('🌐 Chrome automation enabled'));
425492
}
@@ -529,8 +596,112 @@ class ClaudeSM {
529596
program
530597
.name('claude-sm')
531598
.description('Claude with StackMemory context and worktree isolation')
532-
.version('1.0.0')
599+
.version('1.0.0');
600+
601+
// Config subcommand
602+
const configCmd = program
603+
.command('config')
604+
.description('Manage claude-sm defaults');
605+
606+
configCmd
607+
.command('show')
608+
.description('Show current default settings')
609+
.action(() => {
610+
const config = loadSMConfig();
611+
console.log(chalk.blue('claude-sm defaults:'));
612+
console.log(
613+
` defaultWorktree: ${config.defaultWorktree ? chalk.green('true') : chalk.gray('false')}`
614+
);
615+
console.log(
616+
` defaultSandbox: ${config.defaultSandbox ? chalk.green('true') : chalk.gray('false')}`
617+
);
618+
console.log(
619+
` defaultChrome: ${config.defaultChrome ? chalk.green('true') : chalk.gray('false')}`
620+
);
621+
console.log(
622+
` defaultTracing: ${config.defaultTracing ? chalk.green('true') : chalk.gray('false')}`
623+
);
624+
console.log(
625+
` defaultRemote: ${config.defaultRemote ? chalk.green('true') : chalk.gray('false')}`
626+
);
627+
console.log(chalk.gray(`\nConfig: ${getConfigPath()}`));
628+
});
629+
630+
configCmd
631+
.command('set <key> <value>')
632+
.description('Set a default (e.g., set worktree true)')
633+
.action((key: string, value: string) => {
634+
const config = loadSMConfig();
635+
const boolValue = value === 'true' || value === '1' || value === 'on';
636+
637+
const keyMap: Record<string, keyof ClaudeSMConfig> = {
638+
worktree: 'defaultWorktree',
639+
sandbox: 'defaultSandbox',
640+
chrome: 'defaultChrome',
641+
tracing: 'defaultTracing',
642+
remote: 'defaultRemote',
643+
};
644+
645+
const configKey = keyMap[key];
646+
if (!configKey) {
647+
console.log(chalk.red(`Unknown key: ${key}`));
648+
console.log(
649+
chalk.gray('Valid keys: worktree, sandbox, chrome, tracing, remote')
650+
);
651+
process.exit(1);
652+
}
653+
654+
config[configKey] = boolValue;
655+
saveSMConfig(config);
656+
console.log(chalk.green(`Set ${key} = ${boolValue}`));
657+
});
658+
659+
configCmd
660+
.command('worktree-on')
661+
.description('Enable worktree mode by default')
662+
.action(() => {
663+
const config = loadSMConfig();
664+
config.defaultWorktree = true;
665+
saveSMConfig(config);
666+
console.log(chalk.green('Worktree mode enabled by default'));
667+
});
668+
669+
configCmd
670+
.command('worktree-off')
671+
.description('Disable worktree mode by default')
672+
.action(() => {
673+
const config = loadSMConfig();
674+
config.defaultWorktree = false;
675+
saveSMConfig(config);
676+
console.log(chalk.green('Worktree mode disabled by default'));
677+
});
678+
679+
configCmd
680+
.command('remote-on')
681+
.description('Enable remote mode by default (WhatsApp for all questions)')
682+
.action(() => {
683+
const config = loadSMConfig();
684+
config.defaultRemote = true;
685+
saveSMConfig(config);
686+
console.log(chalk.green('Remote mode enabled by default'));
687+
});
688+
689+
configCmd
690+
.command('remote-off')
691+
.description('Disable remote mode by default')
692+
.action(() => {
693+
const config = loadSMConfig();
694+
config.defaultRemote = false;
695+
saveSMConfig(config);
696+
console.log(chalk.green('Remote mode disabled by default'));
697+
});
698+
699+
// Main command (default action when no subcommand)
700+
program
533701
.option('-w, --worktree', 'Create isolated worktree for this instance')
702+
.option('-W, --no-worktree', 'Disable worktree (override default)')
703+
.option('-r, --remote', 'Enable remote mode (WhatsApp for all questions)')
704+
.option('--no-remote', 'Disable remote mode (override default)')
534705
.option('-s, --sandbox', 'Enable sandbox mode (file/network restrictions)')
535706
.option('-c, --chrome', 'Enable Chrome automation')
536707
.option('-a, --auto', 'Automatically detect and apply best settings')

src/core/trace/index.ts

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,6 @@
44
*/
55

66
import type { TraceConfig } from './debug-trace.js';
7-
// Type-safe environment variable access
8-
function getEnv(key: string, defaultValue?: string): string {
9-
const value = process.env[key];
10-
if (value === undefined) {
11-
if (defaultValue !== undefined) return defaultValue;
12-
throw new Error(`Environment variable ${key} is required`);
13-
}
14-
return value;
15-
}
16-
17-
function getOptionalEnv(key: string): string | undefined {
18-
return process.env[key];
19-
}
207

218
export {
229
trace,
@@ -50,60 +37,10 @@ export {
5037

5138
/**
5239
* Initialize tracing based on environment configuration
40+
* Configuration is read directly from env vars by trace decorators
5341
*/
5442
export function initializeTracing(): void {
55-
const config = {
56-
// Main control
57-
DEBUG_TRACE: process.env['DEBUG_TRACE'] === 'true',
58-
STACKMEMORY_DEBUG: process.env['STACKMEMORY_DEBUG'] === 'true',
59-
60-
// Output control
61-
TRACE_OUTPUT: process.env['TRACE_OUTPUT'] || 'console', // console|file|both
62-
TRACE_VERBOSITY: process.env['TRACE_VERBOSITY'] || 'full', // full|errors|summary
63-
64-
// Content control
65-
TRACE_PARAMS: process.env['TRACE_PARAMS'] !== 'false', // Include parameters
66-
TRACE_RESULTS: process.env['TRACE_RESULTS'] !== 'false', // Include results
67-
TRACE_MASK_SENSITIVE: process.env['TRACE_MASK_SENSITIVE'] !== 'false', // Mask sensitive data
68-
69-
// Performance
70-
TRACE_PERF_THRESHOLD: parseInt(
71-
process.env['TRACE_PERF_THRESHOLD'] || '100'
72-
), // ms
73-
TRACE_MEMORY: process.env['TRACE_MEMORY'] === 'true', // Track memory usage
74-
TRACE_MAX_DEPTH: parseInt(process.env['TRACE_MAX_DEPTH'] || '20'), // Max call depth
75-
76-
// Database specific
77-
TRACE_DB: process.env['TRACE_DB'] === 'true', // Enable database tracing
78-
TRACE_DB_SLOW: parseInt(process.env['TRACE_DB_SLOW'] || '100'), // Slow query threshold
79-
80-
// API specific
81-
TRACE_API: process.env['TRACE_API'] === 'true', // Enable API tracing
82-
TRACE_API_SLOW: parseInt(process.env['TRACE_API_SLOW'] || '1000'), // Slow API threshold
83-
};
84-
85-
// Log configuration if debugging is enabled
86-
if (config.DEBUG_TRACE || config.STACKMEMORY_DEBUG) {
87-
console.log('Trace Configuration:', {
88-
enabled: true,
89-
output: config.TRACE_OUTPUT,
90-
verbosity: config.TRACE_VERBOSITY,
91-
includeParams: config.TRACE_PARAMS,
92-
includeResults: config.TRACE_RESULTS,
93-
maskSensitive: config.TRACE_MASK_SENSITIVE,
94-
performanceThreshold: config.TRACE_PERF_THRESHOLD,
95-
captureMemory: config.TRACE_MEMORY,
96-
maxDepth: config.TRACE_MAX_DEPTH,
97-
database: {
98-
enabled: config.TRACE_DB,
99-
slowThreshold: config.TRACE_DB_SLOW,
100-
},
101-
api: {
102-
enabled: config.TRACE_API,
103-
slowThreshold: config.TRACE_API_SLOW,
104-
},
105-
});
106-
}
43+
// No-op - trace config is read from env vars on demand
10744
}
10845

10946
/**

0 commit comments

Comments
 (0)