@stackmemoryai/stackmemory 0.5.48 → 0.5.51

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.
Files changed (35) hide show
  1. package/README.md +17 -3
  2. package/dist/cli/claude-sm.js +246 -5
  3. package/dist/cli/claude-sm.js.map +3 -3
  4. package/dist/cli/commands/handoff.js +5 -5
  5. package/dist/cli/commands/handoff.js.map +2 -2
  6. package/dist/cli/commands/sweep.js +190 -421
  7. package/dist/cli/commands/sweep.js.map +3 -3
  8. package/dist/cli/index.js +8 -2
  9. package/dist/cli/index.js.map +2 -2
  10. package/dist/core/config/feature-flags.js +13 -1
  11. package/dist/core/config/feature-flags.js.map +2 -2
  12. package/dist/core/context/enhanced-rehydration.js +355 -9
  13. package/dist/core/context/enhanced-rehydration.js.map +3 -3
  14. package/dist/core/context/shared-context-layer.js +229 -0
  15. package/dist/core/context/shared-context-layer.js.map +2 -2
  16. package/dist/features/sweep/index.js +20 -0
  17. package/dist/features/sweep/index.js.map +7 -0
  18. package/dist/features/sweep/prediction-client.js +155 -0
  19. package/dist/features/sweep/prediction-client.js.map +7 -0
  20. package/dist/features/sweep/prompt-builder.js +85 -0
  21. package/dist/features/sweep/prompt-builder.js.map +7 -0
  22. package/dist/features/sweep/pty-wrapper.js +171 -0
  23. package/dist/features/sweep/pty-wrapper.js.map +7 -0
  24. package/dist/features/sweep/state-watcher.js +87 -0
  25. package/dist/features/sweep/state-watcher.js.map +7 -0
  26. package/dist/features/sweep/status-bar.js +88 -0
  27. package/dist/features/sweep/status-bar.js.map +7 -0
  28. package/dist/features/sweep/sweep-server-manager.js +226 -0
  29. package/dist/features/sweep/sweep-server-manager.js.map +7 -0
  30. package/dist/features/sweep/tab-interceptor.js +38 -0
  31. package/dist/features/sweep/tab-interceptor.js.map +7 -0
  32. package/dist/features/sweep/types.js +18 -0
  33. package/dist/features/sweep/types.js.map +7 -0
  34. package/package.json +1 -1
  35. package/scripts/test-setup-e2e.sh +154 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # StackMemory
2
2
 
3
- **Lossless, project-scoped memory for AI tools** • v0.5.47
3
+ **Lossless, project-scoped memory for AI tools** • v0.5.51
4
4
 
5
5
  StackMemory is a **production-ready memory runtime** for AI coding tools that preserves full project context across sessions:
6
6
 
@@ -488,14 +488,28 @@ stackmemory mcp-server [--port 3001]
488
488
  - Hosted: **Private beta**
489
489
  - OSS mirror: **Production ready**
490
490
  - MCP integration: **Stable**
491
- - CLI: **v0.5.47** - Zero-config setup, diagnostics, full task/context/Linear management
491
+ - CLI: **v0.5.51** - Zero-config setup, diagnostics, full task/context/Linear management
492
492
  - Two-tier storage: **Complete**
493
- - Test Suite: **490 tests passing**
493
+ - Test Suite: **480 tests passing**
494
494
 
495
495
  ---
496
496
 
497
497
  ## Changelog
498
498
 
499
+ ### v0.5.51 (2026-01-28)
500
+ - **Interactive feature setup**: `claude-sm config setup` wizard to toggle features and install deps on demand
501
+ - Checkbox prompt for all features (Sweep, Greptile, Model Routing, Worktree, WhatsApp, Tracing)
502
+ - Auto-installs `node-pty` when Sweep is enabled
503
+ - Prompts for `GREPTILE_API_KEY` and registers MCP server when Greptile is enabled
504
+ - **Sweep next-edit predictions**: PTY wrapper displays predicted edits as a status bar in Claude Code sessions. Tab to accept, Esc to dismiss. Powered by llama-server via PostToolUse hooks.
505
+ - `claude-sm --sweep` (default: on) or `stackmemory sweep wrap`
506
+ - `node-pty` installed on demand via setup (no longer in optionalDependencies)
507
+ - **Greptile AI code review**: Auto-registers Greptile MCP server for codebase-aware code review.
508
+ - `claude-sm --greptile` (default: on)
509
+ - Requires `GREPTILE_API_KEY` in `.env`
510
+ - Tools: `index_repository`, `query_repository`, `get_repository_info`
511
+ - **Feature flags**: Added `greptile` feature flag to `feature-flags.ts`
512
+
499
513
  ### v0.5.47 (2026-01-27)
500
514
  - **Graceful database failures**: Handles native module version mismatches
501
515
  - **Suppress dotenv logs**: Cleaner terminal output
@@ -25,6 +25,7 @@ import {
25
25
  getModelRouter,
26
26
  loadModelRouterConfig
27
27
  } from "../core/models/model-router.js";
28
+ import { launchWrapper } from "../features/sweep/pty-wrapper.js";
28
29
  import { FallbackMonitor } from "../core/models/fallback-monitor.js";
29
30
  const DEFAULT_SM_CONFIG = {
30
31
  defaultWorktree: false,
@@ -34,7 +35,9 @@ const DEFAULT_SM_CONFIG = {
34
35
  defaultRemote: false,
35
36
  defaultNotifyOnDone: true,
36
37
  defaultWhatsApp: false,
37
- defaultModelRouting: false
38
+ defaultModelRouting: false,
39
+ defaultSweep: true,
40
+ defaultGreptile: true
38
41
  };
39
42
  function getConfigPath() {
40
43
  return path.join(os.homedir(), ".stackmemory", "claude-sm.json");
@@ -79,7 +82,9 @@ class ClaudeSM {
79
82
  verboseTracing: false,
80
83
  sessionStartTime: Date.now(),
81
84
  useModelRouting: this.smConfig.defaultModelRouting,
82
- useThinkingMode: false
85
+ useThinkingMode: false,
86
+ useSweep: this.smConfig.defaultSweep,
87
+ useGreptile: this.smConfig.defaultGreptile
83
88
  };
84
89
  this.stackmemoryPath = this.findStackMemory();
85
90
  this.worktreeScriptPath = path.join(
@@ -149,6 +154,52 @@ class ClaudeSM {
149
154
  }
150
155
  return null;
151
156
  }
157
+ ensureGreptileMcp() {
158
+ const apiKey = process.env["GREPTILE_API_KEY"];
159
+ const ghToken = process.env["GITHUB_TOKEN"];
160
+ if (!apiKey) {
161
+ console.log(
162
+ chalk.gray(" Greptile: disabled (set GREPTILE_API_KEY in .env)")
163
+ );
164
+ return;
165
+ }
166
+ try {
167
+ const result = execSync("claude mcp list 2>/dev/null", {
168
+ encoding: "utf-8"
169
+ });
170
+ if (result.includes("greptile")) {
171
+ console.log(chalk.gray(" Greptile: MCP server registered"));
172
+ return;
173
+ }
174
+ } catch {
175
+ }
176
+ try {
177
+ const cmd = [
178
+ "claude mcp add",
179
+ "--transport http",
180
+ "greptile",
181
+ "https://api.greptile.com/mcp",
182
+ `--header "Authorization: Bearer ${apiKey}"`
183
+ ];
184
+ if (ghToken) {
185
+ cmd.push(`--header "X-GitHub-Token: ${ghToken}"`);
186
+ }
187
+ execSync(cmd.join(" "), { stdio: "ignore" });
188
+ console.log(chalk.cyan(" Greptile: MCP server registered"));
189
+ } catch {
190
+ try {
191
+ const envArgs = [`GREPTILE_API_KEY=${apiKey}`];
192
+ if (ghToken) envArgs.push(`GITHUB_TOKEN=${ghToken}`);
193
+ execSync(
194
+ `claude mcp add greptile -- env ${envArgs.join(" ")} npx greptile-mcp-server`,
195
+ { stdio: "ignore" }
196
+ );
197
+ console.log(chalk.cyan(" Greptile: MCP server registered (stdio)"));
198
+ } catch {
199
+ console.log(chalk.gray(" Greptile: failed to register MCP server"));
200
+ }
201
+ }
202
+ }
152
203
  setupWorktree() {
153
204
  if (!this.config.useWorktree || !this.isGitRepo()) {
154
205
  return null;
@@ -519,6 +570,18 @@ class ClaudeSM {
519
570
  case "--no-model-routing":
520
571
  this.config.useModelRouting = false;
521
572
  break;
573
+ case "--sweep":
574
+ this.config.useSweep = true;
575
+ break;
576
+ case "--no-sweep":
577
+ this.config.useSweep = false;
578
+ break;
579
+ case "--greptile":
580
+ this.config.useGreptile = true;
581
+ break;
582
+ case "--no-greptile":
583
+ this.config.useGreptile = false;
584
+ break;
522
585
  default:
523
586
  claudeArgs.push(arg);
524
587
  }
@@ -661,6 +724,9 @@ class ClaudeSM {
661
724
  );
662
725
  }
663
726
  }
727
+ if (this.config.useGreptile) {
728
+ this.ensureGreptileMcp();
729
+ }
664
730
  if (this.config.useWhatsApp) {
665
731
  console.log(
666
732
  chalk.cyan("\u{1F4F1} WhatsApp mode: notifications + webhook enabled")
@@ -668,6 +734,28 @@ class ClaudeSM {
668
734
  await this.startWhatsAppServices();
669
735
  }
670
736
  console.log();
737
+ if (this.config.useSweep) {
738
+ const claudeBin2 = this.resolveClaudeBin();
739
+ if (!claudeBin2) {
740
+ console.error(chalk.red("Claude CLI not found."));
741
+ process.exit(1);
742
+ return;
743
+ }
744
+ console.log(
745
+ chalk.cyan("[Sweep] Launching Claude with prediction bar...")
746
+ );
747
+ console.log(chalk.gray("\u2500".repeat(42)));
748
+ try {
749
+ await launchWrapper({
750
+ claudeBin: claudeBin2,
751
+ claudeArgs
752
+ });
753
+ } catch (error) {
754
+ console.error(chalk.red(error.message));
755
+ process.exit(1);
756
+ }
757
+ return;
758
+ }
671
759
  console.log(chalk.gray("Starting Claude..."));
672
760
  console.log(chalk.gray("\u2500".repeat(42)));
673
761
  const claudeBin = this.resolveClaudeBin();
@@ -806,6 +894,12 @@ configCmd.command("show").description("Show current default settings").action(()
806
894
  console.log(
807
895
  ` defaultModelRouting: ${config.defaultModelRouting ? chalk.green("true") : chalk.gray("false")}`
808
896
  );
897
+ console.log(
898
+ ` defaultSweep: ${config.defaultSweep ? chalk.green("true") : chalk.gray("false")}`
899
+ );
900
+ console.log(
901
+ ` defaultGreptile: ${config.defaultGreptile ? chalk.green("true") : chalk.gray("false")}`
902
+ );
809
903
  console.log(chalk.gray(`
810
904
  Config: ${getConfigPath()}`));
811
905
  });
@@ -822,14 +916,16 @@ configCmd.command("set <key> <value>").description("Set a default (e.g., set wor
822
916
  notifyondone: "defaultNotifyOnDone",
823
917
  whatsapp: "defaultWhatsApp",
824
918
  "model-routing": "defaultModelRouting",
825
- modelrouting: "defaultModelRouting"
919
+ modelrouting: "defaultModelRouting",
920
+ sweep: "defaultSweep",
921
+ greptile: "defaultGreptile"
826
922
  };
827
923
  const configKey = keyMap[key];
828
924
  if (!configKey) {
829
925
  console.log(chalk.red(`Unknown key: ${key}`));
830
926
  console.log(
831
927
  chalk.gray(
832
- "Valid keys: worktree, sandbox, chrome, tracing, remote, notify-done, whatsapp"
928
+ "Valid keys: worktree, sandbox, chrome, tracing, remote, notify-done, whatsapp, sweep, greptile"
833
929
  )
834
930
  );
835
931
  process.exit(1);
@@ -903,10 +999,155 @@ configCmd.command("model-routing-off").description("Disable model routing by def
903
999
  saveSMConfig(config);
904
1000
  console.log(chalk.green("Model routing disabled by default"));
905
1001
  });
1002
+ configCmd.command("greptile-on").description(
1003
+ "Enable Greptile AI code review by default (requires GREPTILE_API_KEY)"
1004
+ ).action(() => {
1005
+ const config = loadSMConfig();
1006
+ config.defaultGreptile = true;
1007
+ saveSMConfig(config);
1008
+ console.log(chalk.green("Greptile enabled by default"));
1009
+ if (!process.env["GREPTILE_API_KEY"]) {
1010
+ console.log(chalk.gray("Set GREPTILE_API_KEY in .env to activate"));
1011
+ }
1012
+ });
1013
+ configCmd.command("greptile-off").description("Disable Greptile AI code review by default").action(() => {
1014
+ const config = loadSMConfig();
1015
+ config.defaultGreptile = false;
1016
+ saveSMConfig(config);
1017
+ console.log(chalk.green("Greptile disabled by default"));
1018
+ });
1019
+ configCmd.command("setup").description("Interactive feature setup wizard").action(async () => {
1020
+ const inquirer = await import("inquirer");
1021
+ const ora = (await import("ora")).default;
1022
+ const config = loadSMConfig();
1023
+ console.log(chalk.cyan("\nClaude-SM Feature Setup\n"));
1024
+ const features = [
1025
+ {
1026
+ key: "defaultSweep",
1027
+ name: "Sweep",
1028
+ desc: "Next-edit predictions via PTY wrapper (installs node-pty)"
1029
+ },
1030
+ {
1031
+ key: "defaultGreptile",
1032
+ name: "Greptile",
1033
+ desc: "AI code review MCP server (requires GREPTILE_API_KEY)"
1034
+ },
1035
+ {
1036
+ key: "defaultModelRouting",
1037
+ name: "Model Routing",
1038
+ desc: "Route tasks to Qwen/other providers"
1039
+ },
1040
+ {
1041
+ key: "defaultWorktree",
1042
+ name: "Worktree",
1043
+ desc: "Git worktree isolation per instance"
1044
+ },
1045
+ {
1046
+ key: "defaultWhatsApp",
1047
+ name: "WhatsApp",
1048
+ desc: "Notifications and remote control"
1049
+ },
1050
+ {
1051
+ key: "defaultTracing",
1052
+ name: "Tracing",
1053
+ desc: "Debug trace logging"
1054
+ },
1055
+ {
1056
+ key: "defaultNotifyOnDone",
1057
+ name: "Notify on Done",
1058
+ desc: "Notification when session ends"
1059
+ }
1060
+ ];
1061
+ const choices = features.map((f) => ({
1062
+ name: `${f.name} - ${f.desc}`,
1063
+ value: f.key,
1064
+ checked: config[f.key]
1065
+ }));
1066
+ const { selected } = await inquirer.default.prompt([
1067
+ {
1068
+ type: "checkbox",
1069
+ name: "selected",
1070
+ message: "Select features to enable:",
1071
+ choices
1072
+ }
1073
+ ]);
1074
+ const selectedKeys = selected;
1075
+ for (const f of features) {
1076
+ config[f.key] = selectedKeys.includes(f.key);
1077
+ }
1078
+ saveSMConfig(config);
1079
+ if (config.defaultSweep) {
1080
+ let hasPty = false;
1081
+ try {
1082
+ await import("node-pty");
1083
+ hasPty = true;
1084
+ } catch {
1085
+ }
1086
+ if (!hasPty) {
1087
+ const spinner = ora("Installing node-pty...").start();
1088
+ try {
1089
+ execSync("npm install node-pty", {
1090
+ stdio: "ignore",
1091
+ cwd: process.cwd()
1092
+ });
1093
+ spinner.succeed("node-pty installed");
1094
+ } catch {
1095
+ spinner.fail("Failed to install node-pty");
1096
+ console.log(chalk.gray(" Install manually: npm install node-pty"));
1097
+ }
1098
+ }
1099
+ }
1100
+ if (config.defaultGreptile) {
1101
+ const apiKey = process.env["GREPTILE_API_KEY"];
1102
+ if (!apiKey) {
1103
+ const { key } = await inquirer.default.prompt([
1104
+ {
1105
+ type: "password",
1106
+ name: "key",
1107
+ message: "Enter your Greptile API key (from app.greptile.com):",
1108
+ mask: "*"
1109
+ }
1110
+ ]);
1111
+ if (key && key.trim()) {
1112
+ const envPath = path.join(process.cwd(), ".env");
1113
+ const line = `
1114
+ GREPTILE_API_KEY=${key.trim()}
1115
+ `;
1116
+ fs.appendFileSync(envPath, line);
1117
+ process.env["GREPTILE_API_KEY"] = key.trim();
1118
+ console.log(chalk.green(" API key saved to .env"));
1119
+ }
1120
+ }
1121
+ const currentKey = process.env["GREPTILE_API_KEY"];
1122
+ if (currentKey) {
1123
+ try {
1124
+ const result = execSync("claude mcp list 2>/dev/null", {
1125
+ encoding: "utf-8"
1126
+ });
1127
+ if (!result.includes("greptile")) {
1128
+ execSync(
1129
+ `claude mcp add --transport http greptile https://api.greptile.com/mcp --header "Authorization: Bearer ${currentKey}"`,
1130
+ { stdio: "ignore" }
1131
+ );
1132
+ console.log(chalk.green(" Greptile MCP server registered"));
1133
+ }
1134
+ } catch {
1135
+ }
1136
+ }
1137
+ }
1138
+ console.log(chalk.cyan("\nFeature summary:"));
1139
+ for (const f of features) {
1140
+ const on = config[f.key];
1141
+ const mark = on ? chalk.green("ON") : chalk.gray("OFF");
1142
+ console.log(` ${mark} ${f.name}`);
1143
+ }
1144
+ console.log(chalk.gray(`
1145
+ Saved to ${getConfigPath()}`));
1146
+ });
906
1147
  program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").option("-r, --remote", "Enable remote mode (WhatsApp for all questions)").option("--no-remote", "Disable remote mode (override default)").option("-n, --notify-done", "Send WhatsApp notification when session ends").option("--no-notify-done", "Disable notification when session ends").option(
907
1148
  "--whatsapp",
908
1149
  "Enable WhatsApp mode (auto-start webhook + ngrok + notifications)"
909
- ).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").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
1150
+ ).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").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
910
1151
  const claudeSM = new ClaudeSM();
911
1152
  const args = process.argv.slice(2);
912
1153
  await claudeSM.run(args);