@stackmemoryai/stackmemory 0.7.0 → 0.8.1
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/README.md +39 -1
- package/dist/src/cli/claude-sm.js +69 -372
- package/dist/src/cli/claude-sm.js.map +2 -2
- package/dist/src/cli/codex-sm.js +28 -0
- package/dist/src/cli/codex-sm.js.map +2 -2
- package/dist/src/cli/commands/clear.js +10 -15
- package/dist/src/cli/commands/clear.js.map +2 -2
- package/dist/src/cli/commands/daemon.js +31 -0
- package/dist/src/cli/commands/daemon.js.map +2 -2
- package/dist/src/cli/commands/onboard.js +22 -6
- package/dist/src/cli/commands/onboard.js.map +2 -2
- package/dist/src/cli/commands/setup.js +1 -2
- package/dist/src/cli/commands/setup.js.map +2 -2
- package/dist/src/cli/index.js +1 -71
- package/dist/src/cli/index.js.map +3 -3
- package/dist/src/cli/opencode-sm.js +28 -0
- package/dist/src/cli/opencode-sm.js.map +2 -2
- package/dist/src/core/config/feature-flags.js +1 -13
- package/dist/src/core/config/feature-flags.js.map +2 -2
- package/dist/src/core/context/refactored-frame-manager.js +7 -0
- package/dist/src/core/context/refactored-frame-manager.js.map +2 -2
- package/dist/src/core/database/sqlite-adapter.js +1 -1
- package/dist/src/core/database/sqlite-adapter.js.map +2 -2
- package/dist/src/core/session/enhanced-handoff.js +1 -1
- package/dist/src/core/session/enhanced-handoff.js.map +2 -2
- package/dist/src/daemon/daemon-config.js +11 -0
- package/dist/src/daemon/daemon-config.js.map +2 -2
- package/dist/src/daemon/services/memory-service.js +190 -0
- package/dist/src/daemon/services/memory-service.js.map +7 -0
- package/dist/src/daemon/unified-daemon.js +33 -3
- package/dist/src/daemon/unified-daemon.js.map +2 -2
- package/dist/src/features/sweep/pty-wrapper.js +11 -1
- package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
- package/dist/src/hooks/index.js +0 -3
- package/dist/src/hooks/index.js.map +2 -2
- package/dist/src/hooks/schemas.js +0 -117
- package/dist/src/hooks/schemas.js.map +2 -2
- package/dist/src/hooks/session-summary.js.map +1 -1
- package/package.json +2 -2
- package/scripts/smoke-init-db.sh +77 -0
- package/scripts/test-pre-publish-quick.sh +26 -34
- package/scripts/install-notify-hook.sh +0 -84
- package/scripts/setup-notify-webhook.sh +0 -103
- package/templates/claude-hooks/notify-review-hook.js +0 -360
- package/templates/claude-hooks/sms-response-handler.js +0 -174
|
@@ -13,14 +13,6 @@ import { program } from "commander";
|
|
|
13
13
|
import { v4 as uuidv4 } from "uuid";
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
import { initializeTracing, trace } from "../core/trace/index.js";
|
|
16
|
-
import {
|
|
17
|
-
generateSessionSummary,
|
|
18
|
-
formatSummaryMessage
|
|
19
|
-
} from "../hooks/session-summary.js";
|
|
20
|
-
import { sendNotification, loadSMSConfig } from "../hooks/sms-notify.js";
|
|
21
|
-
import { enableAutoSync } from "../hooks/whatsapp-sync.js";
|
|
22
|
-
import { enableCommands } from "../hooks/whatsapp-commands.js";
|
|
23
|
-
import { startScheduler, listSchedules } from "../hooks/whatsapp-scheduler.js";
|
|
24
16
|
import {
|
|
25
17
|
getModelRouter,
|
|
26
18
|
loadModelRouterConfig
|
|
@@ -48,12 +40,9 @@ const DEFAULT_SM_CONFIG = {
|
|
|
48
40
|
defaultSandbox: false,
|
|
49
41
|
defaultChrome: false,
|
|
50
42
|
defaultTracing: true,
|
|
51
|
-
defaultRemote: false,
|
|
52
43
|
defaultNotifyOnDone: true,
|
|
53
|
-
defaultWhatsApp: false,
|
|
54
44
|
defaultModelRouting: false,
|
|
55
45
|
defaultSweep: true,
|
|
56
|
-
defaultGreptile: true,
|
|
57
46
|
defaultGEPA: false
|
|
58
47
|
};
|
|
59
48
|
function getConfigPath() {
|
|
@@ -91,9 +80,7 @@ class ClaudeSM {
|
|
|
91
80
|
useSandbox: this.smConfig.defaultSandbox,
|
|
92
81
|
useChrome: this.smConfig.defaultChrome,
|
|
93
82
|
useWorktree: this.smConfig.defaultWorktree,
|
|
94
|
-
useRemote: this.smConfig.defaultRemote,
|
|
95
83
|
notifyOnDone: this.smConfig.defaultNotifyOnDone,
|
|
96
|
-
useWhatsApp: this.smConfig.defaultWhatsApp,
|
|
97
84
|
contextEnabled: true,
|
|
98
85
|
tracingEnabled: this.smConfig.defaultTracing,
|
|
99
86
|
verboseTracing: false,
|
|
@@ -101,7 +88,6 @@ class ClaudeSM {
|
|
|
101
88
|
useModelRouting: this.smConfig.defaultModelRouting,
|
|
102
89
|
useThinkingMode: false,
|
|
103
90
|
useSweep: this.smConfig.defaultSweep,
|
|
104
|
-
useGreptile: this.smConfig.defaultGreptile,
|
|
105
91
|
useGEPA: this.smConfig.defaultGEPA
|
|
106
92
|
};
|
|
107
93
|
this.stackmemoryPath = this.findStackMemory();
|
|
@@ -114,6 +100,33 @@ class ClaudeSM {
|
|
|
114
100
|
fs.mkdirSync(this.claudeConfigDir, { recursive: true });
|
|
115
101
|
}
|
|
116
102
|
}
|
|
103
|
+
getRepoRoot() {
|
|
104
|
+
try {
|
|
105
|
+
const root = execSync("git rev-parse --show-toplevel", {
|
|
106
|
+
encoding: "utf8"
|
|
107
|
+
}).trim();
|
|
108
|
+
return root || null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
ensureInitialized() {
|
|
114
|
+
try {
|
|
115
|
+
const root = this.getRepoRoot();
|
|
116
|
+
const dir = root || process.cwd();
|
|
117
|
+
const dbPath = path.join(dir, ".stackmemory", "context.db");
|
|
118
|
+
if (!fs.existsSync(dbPath)) {
|
|
119
|
+
console.log(
|
|
120
|
+
chalk.blue("\u{1F4E6} Initializing StackMemory for this project...")
|
|
121
|
+
);
|
|
122
|
+
execSync(`${this.stackmemoryPath} init`, {
|
|
123
|
+
cwd: dir,
|
|
124
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
117
130
|
generateInstanceId() {
|
|
118
131
|
return uuidv4().substring(0, 8);
|
|
119
132
|
}
|
|
@@ -239,52 +252,6 @@ class ClaudeSM {
|
|
|
239
252
|
}
|
|
240
253
|
this.gepaProcesses = [];
|
|
241
254
|
}
|
|
242
|
-
ensureGreptileMcp() {
|
|
243
|
-
const apiKey = process.env["GREPTILE_API_KEY"];
|
|
244
|
-
const ghToken = process.env["GITHUB_TOKEN"];
|
|
245
|
-
if (!apiKey) {
|
|
246
|
-
console.log(
|
|
247
|
-
chalk.gray(" Greptile: disabled (set GREPTILE_API_KEY in .env)")
|
|
248
|
-
);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
const result = execSync("claude mcp list 2>/dev/null", {
|
|
253
|
-
encoding: "utf-8"
|
|
254
|
-
});
|
|
255
|
-
if (result.includes("greptile")) {
|
|
256
|
-
console.log(chalk.gray(" Greptile: MCP server registered"));
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
} catch {
|
|
260
|
-
}
|
|
261
|
-
try {
|
|
262
|
-
const cmd = [
|
|
263
|
-
"claude mcp add",
|
|
264
|
-
"--transport http",
|
|
265
|
-
"greptile",
|
|
266
|
-
"https://api.greptile.com/mcp",
|
|
267
|
-
`--header "Authorization: Bearer ${apiKey}"`
|
|
268
|
-
];
|
|
269
|
-
if (ghToken) {
|
|
270
|
-
cmd.push(`--header "X-GitHub-Token: ${ghToken}"`);
|
|
271
|
-
}
|
|
272
|
-
execSync(cmd.join(" "), { stdio: "ignore" });
|
|
273
|
-
console.log(chalk.cyan(" Greptile: MCP server registered"));
|
|
274
|
-
} catch {
|
|
275
|
-
try {
|
|
276
|
-
const envArgs = [`GREPTILE_API_KEY=${apiKey}`];
|
|
277
|
-
if (ghToken) envArgs.push(`GITHUB_TOKEN=${ghToken}`);
|
|
278
|
-
execSync(
|
|
279
|
-
`claude mcp add greptile -- env ${envArgs.join(" ")} npx greptile-mcp-server`,
|
|
280
|
-
{ stdio: "ignore" }
|
|
281
|
-
);
|
|
282
|
-
console.log(chalk.cyan(" Greptile: MCP server registered (stdio)"));
|
|
283
|
-
} catch {
|
|
284
|
-
console.log(chalk.gray(" Greptile: failed to register MCP server"));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
255
|
setupWorktree() {
|
|
289
256
|
if (!this.config.useWorktree || !this.isGitRepo()) {
|
|
290
257
|
return null;
|
|
@@ -347,19 +314,35 @@ class ClaudeSM {
|
|
|
347
314
|
} catch {
|
|
348
315
|
}
|
|
349
316
|
}
|
|
317
|
+
getHandoffContent() {
|
|
318
|
+
if (!this.config.contextEnabled) return null;
|
|
319
|
+
try {
|
|
320
|
+
const handoffPath = path.join(
|
|
321
|
+
process.cwd(),
|
|
322
|
+
".stackmemory",
|
|
323
|
+
"last-handoff.md"
|
|
324
|
+
);
|
|
325
|
+
if (fs.existsSync(handoffPath)) {
|
|
326
|
+
const content = fs.readFileSync(handoffPath, "utf8").trim();
|
|
327
|
+
if (content.length > 0) {
|
|
328
|
+
return content.length > 8e3 ? content.substring(0, 8e3) + "\n\n[...truncated]" : content;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
350
335
|
loadContext() {
|
|
351
336
|
if (!this.config.contextEnabled) return;
|
|
352
337
|
try {
|
|
353
|
-
console.log(chalk.blue("\u{1F4DA} Loading previous context..."));
|
|
354
338
|
const cmd = `${this.stackmemoryPath} context show`;
|
|
355
339
|
const output = execSync(cmd, {
|
|
356
340
|
encoding: "utf8",
|
|
357
341
|
stdio: ["pipe", "pipe", "pipe"]
|
|
358
|
-
// Capture stderr to suppress errors
|
|
359
342
|
});
|
|
360
343
|
const lines = output.trim().split("\n").filter((l) => l.trim());
|
|
361
344
|
if (lines.length > 3) {
|
|
362
|
-
console.log(chalk.gray("Context stack loaded"));
|
|
345
|
+
console.log(chalk.gray(" Context stack loaded"));
|
|
363
346
|
}
|
|
364
347
|
} catch {
|
|
365
348
|
}
|
|
@@ -394,166 +377,10 @@ class ClaudeSM {
|
|
|
394
377
|
);
|
|
395
378
|
}
|
|
396
379
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
console.log(chalk.
|
|
400
|
-
|
|
401
|
-
const smsConfig = loadSMSConfig();
|
|
402
|
-
if (!smsConfig.enabled) {
|
|
403
|
-
console.log(
|
|
404
|
-
chalk.yellow(" Notifications disabled. Run: stackmemory notify enable")
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
try {
|
|
408
|
-
enableAutoSync();
|
|
409
|
-
console.log(
|
|
410
|
-
chalk.green(" \u2713 Auto-sync enabled (digests on frame close)")
|
|
411
|
-
);
|
|
412
|
-
} catch {
|
|
413
|
-
console.log(chalk.gray(" Auto-sync: skipped"));
|
|
414
|
-
}
|
|
415
|
-
try {
|
|
416
|
-
enableCommands();
|
|
417
|
-
console.log(chalk.green(" \u2713 Command processing enabled"));
|
|
418
|
-
console.log(
|
|
419
|
-
chalk.gray(" Reply: status, tasks, context, help, build, test, lint")
|
|
420
|
-
);
|
|
421
|
-
} catch {
|
|
422
|
-
console.log(chalk.gray(" Command processing: skipped"));
|
|
423
|
-
}
|
|
424
|
-
try {
|
|
425
|
-
const schedules = listSchedules();
|
|
426
|
-
if (schedules.length > 0) {
|
|
427
|
-
startScheduler();
|
|
428
|
-
console.log(
|
|
429
|
-
chalk.green(` \u2713 Scheduler started (${schedules.length} schedules)`)
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
} catch {
|
|
433
|
-
}
|
|
434
|
-
const webhookRunning = await fetch(
|
|
435
|
-
`http://localhost:${WEBHOOK_PORT}/health`
|
|
436
|
-
).then((r) => r.ok).catch(() => false);
|
|
437
|
-
if (!webhookRunning) {
|
|
438
|
-
const webhookPath = path.join(__dirname, "../hooks/sms-webhook.js");
|
|
439
|
-
const webhookProcess = spawn("node", [webhookPath], {
|
|
440
|
-
detached: true,
|
|
441
|
-
stdio: "ignore",
|
|
442
|
-
env: { ...process.env, SMS_WEBHOOK_PORT: String(WEBHOOK_PORT) }
|
|
443
|
-
});
|
|
444
|
-
webhookProcess.unref();
|
|
445
|
-
console.log(
|
|
446
|
-
chalk.green(` \u2713 Webhook server started (port ${WEBHOOK_PORT})`)
|
|
447
|
-
);
|
|
448
|
-
} else {
|
|
449
|
-
console.log(
|
|
450
|
-
chalk.green(` \u2713 Webhook already running (port ${WEBHOOK_PORT})`)
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
const ngrokRunning = await fetch("http://localhost:4040/api/tunnels").then((r) => r.ok).catch(() => false);
|
|
454
|
-
if (!ngrokRunning) {
|
|
455
|
-
const ngrokProcess = spawn("ngrok", ["http", String(WEBHOOK_PORT)], {
|
|
456
|
-
detached: true,
|
|
457
|
-
stdio: "ignore"
|
|
458
|
-
});
|
|
459
|
-
ngrokProcess.unref();
|
|
460
|
-
console.log(chalk.gray(" Starting ngrok tunnel..."));
|
|
461
|
-
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
462
|
-
}
|
|
463
|
-
try {
|
|
464
|
-
const tunnels = await fetch("http://localhost:4040/api/tunnels").then(
|
|
465
|
-
(r) => r.json()
|
|
466
|
-
);
|
|
467
|
-
const publicUrl = tunnels?.tunnels?.[0]?.public_url;
|
|
468
|
-
if (publicUrl) {
|
|
469
|
-
const configDir = path.join(os.homedir(), ".stackmemory");
|
|
470
|
-
const configPath = path.join(configDir, "ngrok-url.txt");
|
|
471
|
-
if (!fs.existsSync(configDir)) {
|
|
472
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
473
|
-
}
|
|
474
|
-
fs.writeFileSync(configPath, publicUrl);
|
|
475
|
-
console.log(chalk.green(` \u2713 ngrok tunnel: ${publicUrl}/sms/incoming`));
|
|
476
|
-
}
|
|
477
|
-
} catch {
|
|
478
|
-
console.log(chalk.yellow(" ngrok: waiting for tunnel..."));
|
|
479
|
-
}
|
|
480
|
-
if (smsConfig.pendingPrompts.length > 0) {
|
|
481
|
-
console.log();
|
|
482
|
-
console.log(
|
|
483
|
-
chalk.yellow(
|
|
484
|
-
` \u23F3 ${smsConfig.pendingPrompts.length} pending prompt(s) awaiting response:`
|
|
485
|
-
)
|
|
486
|
-
);
|
|
487
|
-
smsConfig.pendingPrompts.slice(0, 3).forEach((p, i) => {
|
|
488
|
-
const msg = p.message.length > 40 ? p.message.slice(0, 40) + "..." : p.message;
|
|
489
|
-
console.log(chalk.gray(` ${i + 1}. [${p.id}] ${msg}`));
|
|
490
|
-
});
|
|
491
|
-
if (smsConfig.pendingPrompts.length > 3) {
|
|
492
|
-
console.log(
|
|
493
|
-
chalk.gray(` ... and ${smsConfig.pendingPrompts.length - 3} more`)
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
console.log();
|
|
498
|
-
console.log(chalk.gray(" Quick actions from WhatsApp:"));
|
|
499
|
-
console.log(chalk.gray(' "status" - session status'));
|
|
500
|
-
console.log(chalk.gray(' "context" - current frame digest'));
|
|
501
|
-
console.log(chalk.gray(' "1", "2" - respond to prompts'));
|
|
502
|
-
console.log(chalk.gray("\u2500".repeat(42)));
|
|
503
|
-
}
|
|
504
|
-
async sendDoneNotification(exitCode) {
|
|
505
|
-
try {
|
|
506
|
-
const context = {
|
|
507
|
-
instanceId: this.config.instanceId,
|
|
508
|
-
exitCode,
|
|
509
|
-
sessionStartTime: this.config.sessionStartTime,
|
|
510
|
-
worktreePath: this.config.worktreePath,
|
|
511
|
-
branch: this.config.branch,
|
|
512
|
-
task: this.config.task
|
|
513
|
-
};
|
|
514
|
-
const summary = await generateSessionSummary(context);
|
|
515
|
-
const message = formatSummaryMessage(summary, this.config.instanceId);
|
|
516
|
-
console.log(chalk.cyan("\nSending session summary via WhatsApp..."));
|
|
517
|
-
let options = summary.suggestions.slice(0, 4).map((s) => ({
|
|
518
|
-
key: s.key,
|
|
519
|
-
label: s.label,
|
|
520
|
-
action: s.action
|
|
521
|
-
}));
|
|
522
|
-
if (options.length < 2) {
|
|
523
|
-
const defaults = [
|
|
524
|
-
{ key: "1", label: "Start new session", action: "claude-sm" },
|
|
525
|
-
{
|
|
526
|
-
key: "2",
|
|
527
|
-
label: "View logs",
|
|
528
|
-
action: "tail -30 ~/.claude/logs/*.log"
|
|
529
|
-
}
|
|
530
|
-
];
|
|
531
|
-
options = defaults.slice(0, 2 - options.length).concat(options);
|
|
532
|
-
options.forEach((o, i) => o.key = String(i + 1));
|
|
533
|
-
}
|
|
534
|
-
const result = await sendNotification({
|
|
535
|
-
type: "task_complete",
|
|
536
|
-
title: `Claude Session ${this.config.instanceId}`,
|
|
537
|
-
message,
|
|
538
|
-
prompt: {
|
|
539
|
-
type: "options",
|
|
540
|
-
options
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
if (result.success) {
|
|
544
|
-
console.log(chalk.green("Notification sent successfully"));
|
|
545
|
-
} else {
|
|
546
|
-
console.log(
|
|
547
|
-
chalk.yellow(`Notification not sent: ${result.error || "unknown"}`)
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
} catch (error) {
|
|
551
|
-
console.log(
|
|
552
|
-
chalk.yellow(
|
|
553
|
-
`Could not send notification: ${error instanceof Error ? error.message : "unknown"}`
|
|
554
|
-
)
|
|
555
|
-
);
|
|
556
|
-
}
|
|
380
|
+
notifyDone(exitCode) {
|
|
381
|
+
process.stdout.write("\x07");
|
|
382
|
+
console.log(chalk.gray(`
|
|
383
|
+
Session ended (exit ${exitCode ?? 0})`));
|
|
557
384
|
}
|
|
558
385
|
async run(args) {
|
|
559
386
|
const claudeArgs = [];
|
|
@@ -569,13 +396,6 @@ class ClaudeSM {
|
|
|
569
396
|
case "-W":
|
|
570
397
|
this.config.useWorktree = false;
|
|
571
398
|
break;
|
|
572
|
-
case "--remote":
|
|
573
|
-
case "-r":
|
|
574
|
-
this.config.useRemote = true;
|
|
575
|
-
break;
|
|
576
|
-
case "--no-remote":
|
|
577
|
-
this.config.useRemote = false;
|
|
578
|
-
break;
|
|
579
399
|
case "--notify-done":
|
|
580
400
|
case "-n":
|
|
581
401
|
this.config.notifyOnDone = true;
|
|
@@ -583,13 +403,6 @@ class ClaudeSM {
|
|
|
583
403
|
case "--no-notify-done":
|
|
584
404
|
this.config.notifyOnDone = false;
|
|
585
405
|
break;
|
|
586
|
-
case "--whatsapp":
|
|
587
|
-
this.config.useWhatsApp = true;
|
|
588
|
-
this.config.notifyOnDone = true;
|
|
589
|
-
break;
|
|
590
|
-
case "--no-whatsapp":
|
|
591
|
-
this.config.useWhatsApp = false;
|
|
592
|
-
break;
|
|
593
406
|
case "--sandbox":
|
|
594
407
|
case "-s":
|
|
595
408
|
this.config.useSandbox = true;
|
|
@@ -661,12 +474,6 @@ class ClaudeSM {
|
|
|
661
474
|
case "--no-sweep":
|
|
662
475
|
this.config.useSweep = false;
|
|
663
476
|
break;
|
|
664
|
-
case "--greptile":
|
|
665
|
-
this.config.useGreptile = true;
|
|
666
|
-
break;
|
|
667
|
-
case "--no-greptile":
|
|
668
|
-
this.config.useGreptile = false;
|
|
669
|
-
break;
|
|
670
477
|
case "--gepa":
|
|
671
478
|
this.config.useGEPA = true;
|
|
672
479
|
break;
|
|
@@ -726,6 +533,7 @@ class ClaudeSM {
|
|
|
726
533
|
console.log(chalk.blue("\u2551 Claude + StackMemory + Worktree \u2551"));
|
|
727
534
|
console.log(chalk.blue("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
728
535
|
console.log();
|
|
536
|
+
this.ensureInitialized();
|
|
729
537
|
if (this.isGitRepo()) {
|
|
730
538
|
const branch = this.getCurrentBranch();
|
|
731
539
|
console.log(chalk.gray(`\u{1F4CD} Current branch: ${branch}`));
|
|
@@ -750,19 +558,11 @@ class ClaudeSM {
|
|
|
750
558
|
if (this.config.worktreePath) {
|
|
751
559
|
process.env["CLAUDE_WORKTREE_PATH"] = this.config.worktreePath;
|
|
752
560
|
}
|
|
753
|
-
if (this.config.useRemote) {
|
|
754
|
-
process.env["CLAUDE_REMOTE"] = "1";
|
|
755
|
-
}
|
|
756
561
|
console.log(chalk.gray(`\u{1F916} Instance ID: ${this.config.instanceId}`));
|
|
757
562
|
console.log(chalk.gray(`\u{1F4C1} Working in: ${process.cwd()}`));
|
|
758
563
|
if (this.config.useSandbox) {
|
|
759
564
|
console.log(chalk.yellow("\u{1F512} Sandbox mode enabled"));
|
|
760
565
|
}
|
|
761
|
-
if (this.config.useRemote) {
|
|
762
|
-
console.log(
|
|
763
|
-
chalk.cyan("\u{1F4F1} Remote mode: WhatsApp notifications for all questions")
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
566
|
if (this.config.useChrome) {
|
|
767
567
|
console.log(chalk.yellow("\u{1F310} Chrome automation enabled"));
|
|
768
568
|
}
|
|
@@ -815,17 +615,17 @@ class ClaudeSM {
|
|
|
815
615
|
);
|
|
816
616
|
}
|
|
817
617
|
}
|
|
818
|
-
if (this.config.useGreptile) {
|
|
819
|
-
this.ensureGreptileMcp();
|
|
820
|
-
}
|
|
821
618
|
if (this.config.useGEPA) {
|
|
822
619
|
this.startGEPAWatcher();
|
|
823
620
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
);
|
|
828
|
-
|
|
621
|
+
let initialInput = "";
|
|
622
|
+
const handoffContent = this.getHandoffContent();
|
|
623
|
+
if (handoffContent) {
|
|
624
|
+
const hasResume = claudeArgs.includes("--continue") || claudeArgs.some((a) => a === "--resume");
|
|
625
|
+
if (!hasResume) {
|
|
626
|
+
initialInput = handoffContent;
|
|
627
|
+
console.log(chalk.gray(" Handoff context ready"));
|
|
628
|
+
}
|
|
829
629
|
}
|
|
830
630
|
console.log();
|
|
831
631
|
if (this.config.useSweep) {
|
|
@@ -842,7 +642,8 @@ class ClaudeSM {
|
|
|
842
642
|
try {
|
|
843
643
|
await launchWrapper({
|
|
844
644
|
claudeBin: claudeBin2,
|
|
845
|
-
claudeArgs
|
|
645
|
+
claudeArgs,
|
|
646
|
+
initialInput: initialInput || void 0
|
|
846
647
|
});
|
|
847
648
|
return;
|
|
848
649
|
} catch (error) {
|
|
@@ -856,6 +657,9 @@ class ClaudeSM {
|
|
|
856
657
|
this.config.useSweep = false;
|
|
857
658
|
}
|
|
858
659
|
}
|
|
660
|
+
if (initialInput) {
|
|
661
|
+
claudeArgs.push("--append-system-prompt", initialInput);
|
|
662
|
+
}
|
|
859
663
|
console.log(chalk.gray("Starting Claude..."));
|
|
860
664
|
console.log(chalk.gray("\u2500".repeat(42)));
|
|
861
665
|
const claudeBin = this.resolveClaudeBin();
|
|
@@ -878,14 +682,6 @@ class ClaudeSM {
|
|
|
878
682
|
[auto-fallback] Switching to ${provider}`));
|
|
879
683
|
console.log(chalk.gray(` Reason: ${reason}`));
|
|
880
684
|
console.log(chalk.gray(` Session will continue on ${provider}...`));
|
|
881
|
-
if (this.config.notifyOnDone || this.config.useWhatsApp) {
|
|
882
|
-
sendNotification({
|
|
883
|
-
type: "custom",
|
|
884
|
-
title: "Model Fallback",
|
|
885
|
-
message: `Claude unavailable (${reason}). Switched to ${provider}.`
|
|
886
|
-
}).catch(() => {
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
685
|
}
|
|
890
686
|
});
|
|
891
687
|
const fallbackAvailable = fallbackMonitor.isFallbackAvailable();
|
|
@@ -938,8 +734,8 @@ Session completed on fallback provider: ${status.currentProvider}`
|
|
|
938
734
|
console.log(chalk.blue("Debug Trace Summary:"));
|
|
939
735
|
console.log(chalk.gray(summary));
|
|
940
736
|
}
|
|
941
|
-
if (this.config.notifyOnDone
|
|
942
|
-
|
|
737
|
+
if (this.config.notifyOnDone) {
|
|
738
|
+
this.notifyDone(code);
|
|
943
739
|
}
|
|
944
740
|
if (this.config.worktreePath) {
|
|
945
741
|
console.log();
|
|
@@ -976,11 +772,9 @@ configCmd.command("show").description("Show current default settings").action(()
|
|
|
976
772
|
console.log(chalk.cyan("\n Feature Status"));
|
|
977
773
|
console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
978
774
|
console.log(` Predictive Edit ${config.defaultSweep ? on : off}`);
|
|
979
|
-
console.log(` Code Review ${config.defaultGreptile ? on : off}`);
|
|
980
775
|
console.log(` Prompt Forge ${config.defaultGEPA ? on : off}`);
|
|
981
776
|
console.log(` Model Switcher ${config.defaultModelRouting ? on : off}`);
|
|
982
777
|
console.log(` Safe Branch ${config.defaultWorktree ? on : off}`);
|
|
983
|
-
console.log(` Mobile Sync ${config.defaultWhatsApp ? on : off}`);
|
|
984
778
|
console.log(` Session Insights ${config.defaultTracing ? on : off}`);
|
|
985
779
|
console.log(` Task Alert ${config.defaultNotifyOnDone ? on : off}`);
|
|
986
780
|
console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
@@ -998,14 +792,11 @@ configCmd.command("set <key> <value>").description("Set a default (e.g., set wor
|
|
|
998
792
|
sandbox: "defaultSandbox",
|
|
999
793
|
chrome: "defaultChrome",
|
|
1000
794
|
tracing: "defaultTracing",
|
|
1001
|
-
remote: "defaultRemote",
|
|
1002
795
|
"notify-done": "defaultNotifyOnDone",
|
|
1003
796
|
notifyondone: "defaultNotifyOnDone",
|
|
1004
|
-
whatsapp: "defaultWhatsApp",
|
|
1005
797
|
"model-routing": "defaultModelRouting",
|
|
1006
798
|
modelrouting: "defaultModelRouting",
|
|
1007
799
|
sweep: "defaultSweep",
|
|
1008
|
-
greptile: "defaultGreptile",
|
|
1009
800
|
gepa: "defaultGEPA"
|
|
1010
801
|
};
|
|
1011
802
|
const configKey = keyMap[key];
|
|
@@ -1013,7 +804,7 @@ configCmd.command("set <key> <value>").description("Set a default (e.g., set wor
|
|
|
1013
804
|
console.log(chalk.red(`Unknown key: ${key}`));
|
|
1014
805
|
console.log(
|
|
1015
806
|
chalk.gray(
|
|
1016
|
-
"Valid keys: worktree, sandbox, chrome, tracing,
|
|
807
|
+
"Valid keys: worktree, sandbox, chrome, tracing, notify-done, model-routing, sweep, gepa"
|
|
1017
808
|
)
|
|
1018
809
|
);
|
|
1019
810
|
process.exit(1);
|
|
@@ -1034,19 +825,7 @@ configCmd.command("worktree-off").description("Disable worktree mode by default"
|
|
|
1034
825
|
saveSMConfig(config);
|
|
1035
826
|
console.log(chalk.green("Worktree mode disabled by default"));
|
|
1036
827
|
});
|
|
1037
|
-
configCmd.command("
|
|
1038
|
-
const config = loadSMConfig();
|
|
1039
|
-
config.defaultRemote = true;
|
|
1040
|
-
saveSMConfig(config);
|
|
1041
|
-
console.log(chalk.green("Remote mode enabled by default"));
|
|
1042
|
-
});
|
|
1043
|
-
configCmd.command("remote-off").description("Disable remote mode by default").action(() => {
|
|
1044
|
-
const config = loadSMConfig();
|
|
1045
|
-
config.defaultRemote = false;
|
|
1046
|
-
saveSMConfig(config);
|
|
1047
|
-
console.log(chalk.green("Remote mode disabled by default"));
|
|
1048
|
-
});
|
|
1049
|
-
configCmd.command("notify-done-on").description("Enable WhatsApp notification when session ends (default)").action(() => {
|
|
828
|
+
configCmd.command("notify-done-on").description("Enable bell notification when session ends (default)").action(() => {
|
|
1050
829
|
const config = loadSMConfig();
|
|
1051
830
|
config.defaultNotifyOnDone = true;
|
|
1052
831
|
saveSMConfig(config);
|
|
@@ -1058,20 +837,6 @@ configCmd.command("notify-done-off").description("Disable notification when sess
|
|
|
1058
837
|
saveSMConfig(config);
|
|
1059
838
|
console.log(chalk.green("Notify-on-done disabled by default"));
|
|
1060
839
|
});
|
|
1061
|
-
configCmd.command("whatsapp-on").description("Enable WhatsApp mode by default (auto-starts webhook + ngrok)").action(() => {
|
|
1062
|
-
const config = loadSMConfig();
|
|
1063
|
-
config.defaultWhatsApp = true;
|
|
1064
|
-
config.defaultNotifyOnDone = true;
|
|
1065
|
-
saveSMConfig(config);
|
|
1066
|
-
console.log(chalk.green("WhatsApp mode enabled by default"));
|
|
1067
|
-
console.log(chalk.gray("Sessions will auto-start webhook and ngrok"));
|
|
1068
|
-
});
|
|
1069
|
-
configCmd.command("whatsapp-off").description("Disable WhatsApp mode by default").action(() => {
|
|
1070
|
-
const config = loadSMConfig();
|
|
1071
|
-
config.defaultWhatsApp = false;
|
|
1072
|
-
saveSMConfig(config);
|
|
1073
|
-
console.log(chalk.green("WhatsApp mode disabled by default"));
|
|
1074
|
-
});
|
|
1075
840
|
configCmd.command("model-routing-on").description(
|
|
1076
841
|
"Enable model routing by default (route tasks to Qwen/other models)"
|
|
1077
842
|
).action(() => {
|
|
@@ -1087,23 +852,6 @@ configCmd.command("model-routing-off").description("Disable model routing by def
|
|
|
1087
852
|
saveSMConfig(config);
|
|
1088
853
|
console.log(chalk.green("Model routing disabled by default"));
|
|
1089
854
|
});
|
|
1090
|
-
configCmd.command("greptile-on").description(
|
|
1091
|
-
"Enable Greptile AI code review by default (requires GREPTILE_API_KEY)"
|
|
1092
|
-
).action(() => {
|
|
1093
|
-
const config = loadSMConfig();
|
|
1094
|
-
config.defaultGreptile = true;
|
|
1095
|
-
saveSMConfig(config);
|
|
1096
|
-
console.log(chalk.green("Greptile enabled by default"));
|
|
1097
|
-
if (!process.env["GREPTILE_API_KEY"]) {
|
|
1098
|
-
console.log(chalk.gray("Set GREPTILE_API_KEY in .env to activate"));
|
|
1099
|
-
}
|
|
1100
|
-
});
|
|
1101
|
-
configCmd.command("greptile-off").description("Disable Greptile AI code review by default").action(() => {
|
|
1102
|
-
const config = loadSMConfig();
|
|
1103
|
-
config.defaultGreptile = false;
|
|
1104
|
-
saveSMConfig(config);
|
|
1105
|
-
console.log(chalk.green("Greptile disabled by default"));
|
|
1106
|
-
});
|
|
1107
855
|
configCmd.command("gepa-on").description("Enable GEPA auto-optimization of CLAUDE.md on changes").action(() => {
|
|
1108
856
|
const config = loadSMConfig();
|
|
1109
857
|
config.defaultGEPA = true;
|
|
@@ -1130,11 +878,6 @@ configCmd.command("setup").description("Interactive feature setup wizard").actio
|
|
|
1130
878
|
name: "Predictive Edit",
|
|
1131
879
|
desc: "AI-powered next-edit suggestions in real-time"
|
|
1132
880
|
},
|
|
1133
|
-
{
|
|
1134
|
-
key: "defaultGreptile",
|
|
1135
|
-
name: "Code Review",
|
|
1136
|
-
desc: "Automated code review with deep codebase intelligence"
|
|
1137
|
-
},
|
|
1138
881
|
{
|
|
1139
882
|
key: "defaultGEPA",
|
|
1140
883
|
name: "Prompt Forge",
|
|
@@ -1150,11 +893,6 @@ configCmd.command("setup").description("Interactive feature setup wizard").actio
|
|
|
1150
893
|
name: "Safe Branch",
|
|
1151
894
|
desc: "Isolated git worktrees for conflict-free parallel work"
|
|
1152
895
|
},
|
|
1153
|
-
{
|
|
1154
|
-
key: "defaultWhatsApp",
|
|
1155
|
-
name: "Mobile Sync",
|
|
1156
|
-
desc: "Remote control and notifications via WhatsApp"
|
|
1157
|
-
},
|
|
1158
896
|
{
|
|
1159
897
|
key: "defaultTracing",
|
|
1160
898
|
name: "Session Insights",
|
|
@@ -1205,44 +943,6 @@ configCmd.command("setup").description("Interactive feature setup wizard").actio
|
|
|
1205
943
|
}
|
|
1206
944
|
}
|
|
1207
945
|
}
|
|
1208
|
-
if (config.defaultGreptile) {
|
|
1209
|
-
const apiKey = process.env["GREPTILE_API_KEY"];
|
|
1210
|
-
if (!apiKey) {
|
|
1211
|
-
const { key } = await inquirer.default.prompt([
|
|
1212
|
-
{
|
|
1213
|
-
type: "password",
|
|
1214
|
-
name: "key",
|
|
1215
|
-
message: "Enter your Greptile API key (from app.greptile.com):",
|
|
1216
|
-
mask: "*"
|
|
1217
|
-
}
|
|
1218
|
-
]);
|
|
1219
|
-
if (key && key.trim()) {
|
|
1220
|
-
const envPath = path.join(process.cwd(), ".env");
|
|
1221
|
-
const line = `
|
|
1222
|
-
GREPTILE_API_KEY=${key.trim()}
|
|
1223
|
-
`;
|
|
1224
|
-
fs.appendFileSync(envPath, line);
|
|
1225
|
-
process.env["GREPTILE_API_KEY"] = key.trim();
|
|
1226
|
-
console.log(chalk.green(" API key saved to .env"));
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
const currentKey = process.env["GREPTILE_API_KEY"];
|
|
1230
|
-
if (currentKey) {
|
|
1231
|
-
try {
|
|
1232
|
-
const result = execSync("claude mcp list 2>/dev/null", {
|
|
1233
|
-
encoding: "utf-8"
|
|
1234
|
-
});
|
|
1235
|
-
if (!result.includes("greptile")) {
|
|
1236
|
-
execSync(
|
|
1237
|
-
`claude mcp add --transport http greptile https://api.greptile.com/mcp --header "Authorization: Bearer ${currentKey}"`,
|
|
1238
|
-
{ stdio: "ignore" }
|
|
1239
|
-
);
|
|
1240
|
-
console.log(chalk.green(" Greptile MCP server registered"));
|
|
1241
|
-
}
|
|
1242
|
-
} catch {
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
946
|
console.log(chalk.cyan("\nFeature summary:"));
|
|
1247
947
|
for (const f of features) {
|
|
1248
948
|
const on = config[f.key];
|
|
@@ -1361,10 +1061,7 @@ workersCmd.command("kill [id]").description("Kill entire session or send Ctrl-C
|
|
|
1361
1061
|
console.log(chalk.gray("Registry cleared."));
|
|
1362
1062
|
}
|
|
1363
1063
|
});
|
|
1364
|
-
program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").option("-
|
|
1365
|
-
"--whatsapp",
|
|
1366
|
-
"Enable WhatsApp mode (auto-start webhook + ngrok + notifications)"
|
|
1367
|
-
).option("--no-whatsapp", "Disable WhatsApp mode (override default)").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--claude-bin <path>", "Path to claude CLI (or use CLAUDE_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").option("--think", "Enable thinking mode with Qwen (deep reasoning)").option("--think-hard", "Alias for --think").option("--ultrathink", "Alias for --think").option("--qwen", "Force Qwen provider for this session").option("--openai", "Force OpenAI provider for this session").option("--ollama", "Force Ollama provider for this session").option("--model-routing", "Enable model routing").option("--no-model-routing", "Disable model routing").option("--sweep", "Enable Sweep next-edit predictions (PTY wrapper)").option("--no-sweep", "Disable Sweep predictions").option("--greptile", "Enable Greptile AI code review (MCP server)").option("--no-greptile", "Disable Greptile integration").option("--gepa", "Enable GEPA auto-optimization of CLAUDE.md").option("--no-gepa", "Disable GEPA auto-optimization").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
1064
|
+
program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").option("-n, --notify-done", "Bell notification when session ends").option("--no-notify-done", "Disable notification when session ends").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--claude-bin <path>", "Path to claude CLI (or use CLAUDE_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").option("--think", "Enable thinking mode with Qwen (deep reasoning)").option("--think-hard", "Alias for --think").option("--ultrathink", "Alias for --think").option("--qwen", "Force Qwen provider for this session").option("--openai", "Force OpenAI provider for this session").option("--ollama", "Force Ollama provider for this session").option("--model-routing", "Enable model routing").option("--no-model-routing", "Disable model routing").option("--sweep", "Enable Sweep next-edit predictions (PTY wrapper)").option("--no-sweep", "Disable Sweep predictions").option("--gepa", "Enable GEPA auto-optimization of CLAUDE.md").option("--no-gepa", "Disable GEPA auto-optimization").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
|
|
1368
1065
|
const claudeSM = new ClaudeSM();
|
|
1369
1066
|
const args = process.argv.slice(2);
|
|
1370
1067
|
await claudeSM.run(args);
|