Skip to content

Commit 0fc1edd

Browse files
fix(claude-sm): Fix context loading command and add session summary
- Fix loadContext() to use correct 'context show' command - Suppress stderr output to avoid "unknown command" errors - Add session summary generation on completion - Integrate WhatsApp notification with session context
1 parent 7f0c6be commit 0fc1edd

4 files changed

Lines changed: 534 additions & 48 deletions

File tree

src/cli/claude-sm.ts

Lines changed: 123 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import { program } from 'commander';
1313
import { v4 as uuidv4 } from 'uuid';
1414
import chalk from 'chalk';
1515
import { initializeTracing, trace } from '../core/trace/index.js';
16+
import {
17+
generateSessionSummary,
18+
formatSummaryMessage,
19+
SessionContext,
20+
} from '../hooks/session-summary.js';
21+
import { sendNotification } from '../hooks/sms-notify.js';
1622

1723
// __filename and __dirname are provided by esbuild banner for ESM compatibility
1824

@@ -22,6 +28,7 @@ interface ClaudeSMConfig {
2228
defaultChrome: boolean;
2329
defaultTracing: boolean;
2430
defaultRemote: boolean;
31+
defaultNotifyOnDone: boolean;
2532
}
2633

2734
interface ClaudeConfig {
@@ -31,12 +38,14 @@ interface ClaudeConfig {
3138
useChrome: boolean;
3239
useWorktree: boolean;
3340
useRemote: boolean;
41+
notifyOnDone: boolean;
3442
contextEnabled: boolean;
3543
branch?: string;
3644
task?: string;
3745
tracingEnabled: boolean;
3846
verboseTracing: boolean;
3947
claudeBin?: string;
48+
sessionStartTime: number;
4049
}
4150

4251
const DEFAULT_SM_CONFIG: ClaudeSMConfig = {
@@ -45,6 +54,7 @@ const DEFAULT_SM_CONFIG: ClaudeSMConfig = {
4554
defaultChrome: false,
4655
defaultTracing: true,
4756
defaultRemote: false,
57+
defaultNotifyOnDone: true,
4858
};
4959

5060
function getConfigPath(): string {
@@ -90,9 +100,11 @@ class ClaudeSM {
90100
useChrome: this.smConfig.defaultChrome,
91101
useWorktree: this.smConfig.defaultWorktree,
92102
useRemote: this.smConfig.defaultRemote,
103+
notifyOnDone: this.smConfig.defaultNotifyOnDone,
93104
contextEnabled: true,
94105
tracingEnabled: this.smConfig.defaultTracing,
95106
verboseTracing: false,
107+
sessionStartTime: Date.now(),
96108
};
97109

98110
this.stackmemoryPath = this.findStackMemory();
@@ -269,22 +281,24 @@ class ClaudeSM {
269281
try {
270282
console.log(chalk.blue('📚 Loading previous context...'));
271283

272-
const cmd = `${this.stackmemoryPath} context list --limit 5 --format json`;
273-
const output = execSync(cmd, { encoding: 'utf8' });
274-
const contexts = JSON.parse(output);
275-
276-
if (contexts.length > 0) {
277-
console.log(chalk.gray('Recent context loaded:'));
278-
contexts.forEach(
279-
(ctx: { message: string; metadata?: { timestamp?: string } }) => {
280-
console.log(
281-
chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)
282-
);
283-
}
284-
);
284+
// Use 'context show' command which outputs the current context stack
285+
const cmd = `${this.stackmemoryPath} context show`;
286+
const output = execSync(cmd, {
287+
encoding: 'utf8',
288+
stdio: ['pipe', 'pipe', 'pipe'], // Capture stderr to suppress errors
289+
});
290+
291+
// Check if we got meaningful output (not empty or just headers)
292+
const lines = output
293+
.trim()
294+
.split('\n')
295+
.filter((l) => l.trim());
296+
if (lines.length > 3) {
297+
// Has content beyond headers
298+
console.log(chalk.gray('Context stack loaded'));
285299
}
286300
} catch {
287-
// Silently continue
301+
// Silently continue - context loading is optional
288302
}
289303
}
290304

@@ -323,6 +337,58 @@ class ClaudeSM {
323337
}
324338
}
325339

340+
private async sendDoneNotification(exitCode: number | null): Promise<void> {
341+
try {
342+
const context: SessionContext = {
343+
instanceId: this.config.instanceId,
344+
exitCode,
345+
sessionStartTime: this.config.sessionStartTime,
346+
worktreePath: this.config.worktreePath,
347+
branch: this.config.branch,
348+
task: this.config.task,
349+
};
350+
351+
const summary = await generateSessionSummary(context);
352+
const message = formatSummaryMessage(summary);
353+
354+
console.log(chalk.cyan('\nSending session summary via WhatsApp...'));
355+
356+
// Build options from suggestions for interactive response
357+
const options = summary.suggestions.slice(0, 4).map((s) => ({
358+
key: s.key,
359+
label: s.label,
360+
action: s.action,
361+
}));
362+
363+
const result = await sendNotification({
364+
type: 'task_complete',
365+
title: 'Claude Session Complete',
366+
message,
367+
prompt:
368+
options.length > 0
369+
? {
370+
type: 'options',
371+
options,
372+
}
373+
: undefined,
374+
});
375+
376+
if (result.success) {
377+
console.log(chalk.green('Notification sent successfully'));
378+
} else {
379+
console.log(
380+
chalk.yellow(`Notification not sent: ${result.error || 'unknown'}`)
381+
);
382+
}
383+
} catch (error) {
384+
console.log(
385+
chalk.yellow(
386+
`Could not send notification: ${error instanceof Error ? error.message : 'unknown'}`
387+
)
388+
);
389+
}
390+
}
391+
326392
public async run(args: string[]): Promise<void> {
327393
// Parse arguments
328394
const claudeArgs: string[] = [];
@@ -347,6 +413,13 @@ class ClaudeSM {
347413
case '--no-remote':
348414
this.config.useRemote = false;
349415
break;
416+
case '--notify-done':
417+
case '-n':
418+
this.config.notifyOnDone = true;
419+
break;
420+
case '--no-notify-done':
421+
this.config.notifyOnDone = false;
422+
break;
350423
case '--sandbox':
351424
case '-s':
352425
this.config.useSandbox = true;
@@ -545,7 +618,7 @@ class ClaudeSM {
545618
});
546619

547620
// Handle exit
548-
claude.on('exit', (code) => {
621+
claude.on('exit', async (code) => {
549622
// Save final context
550623
this.saveContext('Claude session ended', {
551624
action: 'session_end',
@@ -561,6 +634,11 @@ class ClaudeSM {
561634
console.log(chalk.gray(summary));
562635
}
563636

637+
// Send notification when done (if enabled)
638+
if (this.config.notifyOnDone || this.config.useRemote) {
639+
await this.sendDoneNotification(code);
640+
}
641+
564642
// Offer to clean up worktree
565643
if (this.config.worktreePath) {
566644
console.log();
@@ -624,6 +702,9 @@ configCmd
624702
console.log(
625703
` defaultRemote: ${config.defaultRemote ? chalk.green('true') : chalk.gray('false')}`
626704
);
705+
console.log(
706+
` defaultNotifyOnDone: ${config.defaultNotifyOnDone ? chalk.green('true') : chalk.gray('false')}`
707+
);
627708
console.log(chalk.gray(`\nConfig: ${getConfigPath()}`));
628709
});
629710

@@ -640,13 +721,17 @@ configCmd
640721
chrome: 'defaultChrome',
641722
tracing: 'defaultTracing',
642723
remote: 'defaultRemote',
724+
'notify-done': 'defaultNotifyOnDone',
725+
notifyondone: 'defaultNotifyOnDone',
643726
};
644727

645728
const configKey = keyMap[key];
646729
if (!configKey) {
647730
console.log(chalk.red(`Unknown key: ${key}`));
648731
console.log(
649-
chalk.gray('Valid keys: worktree, sandbox, chrome, tracing, remote')
732+
chalk.gray(
733+
'Valid keys: worktree, sandbox, chrome, tracing, remote, notify-done'
734+
)
650735
);
651736
process.exit(1);
652737
}
@@ -696,12 +781,34 @@ configCmd
696781
console.log(chalk.green('Remote mode disabled by default'));
697782
});
698783

784+
configCmd
785+
.command('notify-done-on')
786+
.description('Enable WhatsApp notification when session ends (default)')
787+
.action(() => {
788+
const config = loadSMConfig();
789+
config.defaultNotifyOnDone = true;
790+
saveSMConfig(config);
791+
console.log(chalk.green('Notify-on-done enabled by default'));
792+
});
793+
794+
configCmd
795+
.command('notify-done-off')
796+
.description('Disable notification when session ends')
797+
.action(() => {
798+
const config = loadSMConfig();
799+
config.defaultNotifyOnDone = false;
800+
saveSMConfig(config);
801+
console.log(chalk.green('Notify-on-done disabled by default'));
802+
});
803+
699804
// Main command (default action when no subcommand)
700805
program
701806
.option('-w, --worktree', 'Create isolated worktree for this instance')
702807
.option('-W, --no-worktree', 'Disable worktree (override default)')
703808
.option('-r, --remote', 'Enable remote mode (WhatsApp for all questions)')
704809
.option('--no-remote', 'Disable remote mode (override default)')
810+
.option('-n, --notify-done', 'Send WhatsApp notification when session ends')
811+
.option('--no-notify-done', 'Disable notification when session ends')
705812
.option('-s, --sandbox', 'Enable sandbox mode (file/network restrictions)')
706813
.option('-c, --chrome', 'Enable Chrome automation')
707814
.option('-a, --auto', 'Automatically detect and apply best settings')

0 commit comments

Comments
 (0)