@stackmemoryai/stackmemory 1.2.2 → 1.2.6

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 (77) hide show
  1. package/README.md +10 -6
  2. package/dist/src/cli/claude-sm.js +33 -4
  3. package/dist/src/cli/codex-sm-danger.js +4 -1
  4. package/dist/src/cli/codex-sm.js +6 -8
  5. package/dist/src/cli/commands/config.js +0 -81
  6. package/dist/src/cli/commands/context-rehydrate.js +133 -47
  7. package/dist/src/cli/commands/db.js +35 -8
  8. package/dist/src/cli/commands/handoff.js +1 -1
  9. package/dist/src/cli/commands/linear.js +9 -0
  10. package/dist/src/cli/commands/ralph.js +2 -2
  11. package/dist/src/cli/commands/setup.js +2 -2
  12. package/dist/src/cli/commands/signup.js +3 -1
  13. package/dist/src/cli/commands/skills.js +108 -1
  14. package/dist/src/cli/commands/storage-tier.js +26 -8
  15. package/dist/src/cli/index.js +1 -57
  16. package/dist/src/core/config/feature-flags.js +0 -4
  17. package/dist/src/core/context/dual-stack-manager.js +10 -3
  18. package/dist/src/core/context/frame-database.js +32 -0
  19. package/dist/src/core/context/frame-handoff-manager.js +2 -2
  20. package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
  21. package/dist/src/core/context/index.js +2 -2
  22. package/dist/src/core/database/sqlite-adapter.js +161 -1
  23. package/dist/src/core/digest/frame-digest-integration.js +1 -1
  24. package/dist/src/core/digest/index.js +1 -1
  25. package/dist/src/core/execution/parallel-executor.js +5 -1
  26. package/dist/src/core/projects/project-isolation.js +18 -4
  27. package/dist/src/core/security/index.js +2 -0
  28. package/dist/src/core/security/input-sanitizer.js +23 -0
  29. package/dist/src/core/utils/update-checker.js +10 -6
  30. package/dist/src/daemon/daemon-config.js +2 -1
  31. package/dist/src/daemon/services/auto-save-service.js +121 -0
  32. package/dist/src/daemon/services/maintenance-service.js +76 -1
  33. package/dist/src/features/sweep/prompt-builder.js +2 -2
  34. package/dist/src/hooks/daemon.js +0 -8
  35. package/dist/src/integrations/linear/config.js +3 -1
  36. package/dist/src/integrations/linear/sync.js +18 -5
  37. package/dist/src/integrations/linear/webhook.js +0 -16
  38. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
  39. package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
  40. package/dist/src/integrations/mcp/handlers/index.js +55 -9
  41. package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
  42. package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
  43. package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
  44. package/dist/src/integrations/mcp/index.js +2 -2
  45. package/dist/src/integrations/mcp/refactored-server.js +31 -10
  46. package/dist/src/integrations/mcp/server.js +0 -130
  47. package/dist/src/integrations/mcp/tool-definitions.js +196 -67
  48. package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
  49. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
  50. package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
  51. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
  52. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
  53. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
  54. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
  55. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
  56. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
  57. package/dist/src/skills/claude-skills.js +37 -103
  58. package/dist/src/skills/theory-skill.js +191 -0
  59. package/dist/src/utils/hook-installer.js +16 -0
  60. package/package.json +6 -6
  61. package/scripts/install-claude-hooks-auto.js +8 -0
  62. package/templates/claude-hooks/cord-trace.js +225 -0
  63. package/templates/claude-hooks/theory-capture.js +100 -0
  64. package/dist/src/core/config/storage-config.js +0 -114
  65. package/dist/src/core/storage/chromadb-adapter.js +0 -379
  66. package/dist/src/hooks/graphiti-hooks.js +0 -253
  67. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
  68. package/dist/src/integrations/graphiti/client.js +0 -115
  69. package/dist/src/integrations/graphiti/config.js +0 -17
  70. package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
  71. package/dist/src/integrations/graphiti/types.js +0 -4
  72. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
  73. package/dist/src/skills/repo-ingestion-skill.js +0 -631
  74. package/templates/claude-hooks/chromadb-wrapper +0 -21
  75. /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
  76. /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
  77. /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
@@ -6,7 +6,13 @@ import inquirer from "inquirer";
6
6
  import chalk from "chalk";
7
7
  import { homedir } from "os";
8
8
  import { join } from "path";
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
9
+ import {
10
+ existsSync,
11
+ mkdirSync,
12
+ readFileSync,
13
+ writeFileSync,
14
+ unlinkSync
15
+ } from "fs";
10
16
  import { Pool } from "pg";
11
17
  function loadConfig() {
12
18
  const cfgDir = join(homedir(), ".stackmemory");
@@ -39,8 +45,14 @@ function registerLogoutCommand(program) {
39
45
  if (existsSync(envPath)) unlinkSync(envPath);
40
46
  } catch {
41
47
  }
42
- console.log(chalk.green("\u2713 Switched to local storage and cleared hosted credentials."));
43
- console.log(chalk.gray("Start the server without DATABASE_URL to use local SQLite."));
48
+ console.log(
49
+ chalk.green(
50
+ "\u2713 Switched to local storage and cleared hosted credentials."
51
+ )
52
+ );
53
+ console.log(
54
+ chalk.gray("Start the server without DATABASE_URL to use local SQLite.")
55
+ );
44
56
  });
45
57
  }
46
58
  function registerDbCommands(program) {
@@ -75,7 +87,12 @@ function registerDbCommands(program) {
75
87
  return;
76
88
  }
77
89
  const { openSignup } = await inquirer.prompt([
78
- { type: "confirm", name: "openSignup", message: "Open hosted signup/login page?", default: false }
90
+ {
91
+ type: "confirm",
92
+ name: "openSignup",
93
+ message: "Open hosted signup/login page?",
94
+ default: false
95
+ }
79
96
  ]);
80
97
  if (openSignup) {
81
98
  try {
@@ -96,7 +113,9 @@ function registerDbCommands(program) {
96
113
  const ok = await testPostgres(url);
97
114
  if (!ok) {
98
115
  console.log(chalk.red("failed"));
99
- console.log(chalk.red("\u2717 Could not connect to Postgres with provided URL."));
116
+ console.log(
117
+ chalk.red("\u2717 Could not connect to Postgres with provided URL.")
118
+ );
100
119
  return;
101
120
  }
102
121
  console.log(chalk.green("ok"));
@@ -107,7 +126,9 @@ function registerDbCommands(program) {
107
126
  DATABASE_URL=${url}
108
127
  `);
109
128
  console.log(chalk.green("\u2713 Switched to hosted database."));
110
- console.log(chalk.gray("Tip: export DATABASE_URL before starting the server."));
129
+ console.log(
130
+ chalk.gray("Tip: export DATABASE_URL before starting the server.")
131
+ );
111
132
  });
112
133
  db.command("status").description("Show current database mode and connection status").action(async () => {
113
134
  const { cfgDir, cfg } = loadConfig();
@@ -116,7 +137,11 @@ DATABASE_URL=${url}
116
137
  if (mode === "hosted") {
117
138
  const url = process.env.DATABASE_URL || cfg.database?.url || "";
118
139
  if (!url) {
119
- console.log(chalk.yellow('DATABASE_URL not set and not found in config. Run "stackmemory login".'));
140
+ console.log(
141
+ chalk.yellow(
142
+ 'DATABASE_URL not set and not found in config. Run "stackmemory login".'
143
+ )
144
+ );
120
145
  return;
121
146
  }
122
147
  const masked = maskDsn(url);
@@ -126,7 +151,9 @@ DATABASE_URL=${url}
126
151
  } else {
127
152
  const sqlitePath = join(cfgDir, "railway.db");
128
153
  const exists = existsSync(sqlitePath);
129
- console.log(`Local SQLite path: ${sqlitePath} (${exists ? "exists" : "will be created at first run"})`);
154
+ console.log(
155
+ `Local SQLite path: ${sqlitePath} (${exists ? "exists" : "will be created at first run"})`
156
+ );
130
157
  }
131
158
  });
132
159
  }
@@ -19,7 +19,7 @@ import { z } from "zod";
19
19
  import { FrameManager } from "../../core/context/index.js";
20
20
  import { LinearTaskManager } from "../../features/tasks/linear-task-manager.js";
21
21
  import { logger } from "../../core/monitoring/logger.js";
22
- import { EnhancedHandoffGenerator } from "../../core/session/enhanced-handoff.js";
22
+ import { EnhancedHandoffGenerator } from "../../core/session/handoff.js";
23
23
  const countTokens = (text) => Math.ceil(text.length / 3.5);
24
24
  const MAX_HANDOFF_VERSIONS = 10;
25
25
  function saveVersionedHandoff(projectRoot, branch, content) {
@@ -289,6 +289,15 @@ ${displayTasks.length} shown, ${tasks.length} total tasks`
289
289
  authManager,
290
290
  config
291
291
  );
292
+ if (!syncEngine.isConfigured) {
293
+ console.log(
294
+ chalk.gray(
295
+ '\u2139 Linear API key not configured \u2014 skipping sync. Set LINEAR_API_KEY or run "stackmemory linear setup".'
296
+ )
297
+ );
298
+ db.close();
299
+ return;
300
+ }
292
301
  console.log(chalk.yellow("\u{1F504} Syncing with Linear..."));
293
302
  if (options.dryRun) {
294
303
  console.log(chalk.gray("(Dry run - no changes will be made)"));
@@ -176,8 +176,8 @@ ${contextResponse.context}`;
176
176
  return trace.command("ralph-clean", options, async () => {
177
177
  try {
178
178
  if (!options.keepHistory && existsSync(".ralph/history")) {
179
- const { execSync } = await import("child_process");
180
- execSync("rm -rf .ralph/history");
179
+ const fs = await import("fs");
180
+ fs.rmSync(".ralph/history", { recursive: true, force: true });
181
181
  }
182
182
  if (existsSync(".ralph/work-complete.txt")) {
183
183
  const fs = await import("fs");
@@ -4,7 +4,7 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { Command } from "commander";
6
6
  import chalk from "chalk";
7
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
8
8
  import { join } from "path";
9
9
  import { homedir } from "os";
10
10
  import { execSync } from "child_process";
@@ -366,7 +366,7 @@ function createSetupPluginsCommand() {
366
366
  if (existsSync(targetPath)) {
367
367
  if (options.force) {
368
368
  try {
369
- execSync(`rm -rf "${targetPath}"`, { encoding: "utf-8" });
369
+ rmSync(targetPath, { recursive: true, force: true });
370
370
  } catch {
371
371
  console.log(
372
372
  chalk.red(` [ERROR] ${plugin} - could not remove existing`)
@@ -40,7 +40,9 @@ function registerSignupCommand(program) {
40
40
  console.log(chalk.cyan("\n\u{1F510} Proceeding to login...\n"));
41
41
  await loginCmd.parseAsync(["node", "stackmemory", "login"]);
42
42
  } else {
43
- console.log(chalk.gray("\nWhen ready, run: ") + chalk.cyan("stackmemory login"));
43
+ console.log(
44
+ chalk.gray("\nWhen ready, run: ") + chalk.cyan("stackmemory login")
45
+ );
44
46
  }
45
47
  });
46
48
  }
@@ -795,6 +795,89 @@ ${result.data.diffStat}`));
795
795
  process.exit(1);
796
796
  }
797
797
  });
798
+ const theoryCmd = skillsCmd.command("theory").description("Maintain a living THEORY.MD at repo root");
799
+ theoryCmd.command("show").description("Display current THEORY.MD content").action(async () => {
800
+ try {
801
+ const { context } = await initializeSkillContext();
802
+ const { TheorySkill } = await import("../../skills/theory-skill.js");
803
+ const skill = new TheorySkill(context);
804
+ const result = skill.show();
805
+ if (result.success) {
806
+ console.log(result.message);
807
+ } else {
808
+ console.log(chalk.red("\u2717"), result.message);
809
+ }
810
+ await context.database.disconnect();
811
+ } catch (error) {
812
+ console.error(chalk.red("Error:"), error.message);
813
+ process.exit(1);
814
+ }
815
+ });
816
+ theoryCmd.command("init <problem>").description("Create THEORY.MD with scaffold sections").action(async (problem) => {
817
+ try {
818
+ const { context } = await initializeSkillContext();
819
+ const { TheorySkill } = await import("../../skills/theory-skill.js");
820
+ const skill = new TheorySkill(context);
821
+ const result = skill.init(problem);
822
+ if (result.success) {
823
+ console.log(chalk.green("\u2713"), result.message);
824
+ } else {
825
+ console.log(chalk.red("\u2717"), result.message);
826
+ }
827
+ await context.database.disconnect();
828
+ } catch (error) {
829
+ console.error(chalk.red("Error:"), error.message);
830
+ process.exit(1);
831
+ }
832
+ });
833
+ theoryCmd.command("update <content>").description("Overwrite THEORY.MD with new content").action(async (content) => {
834
+ try {
835
+ const { context } = await initializeSkillContext();
836
+ const { TheorySkill } = await import("../../skills/theory-skill.js");
837
+ const skill = new TheorySkill(context);
838
+ const result = skill.update(content);
839
+ if (result.success) {
840
+ console.log(chalk.green("\u2713"), result.message);
841
+ if (result.data?.warnings?.length > 0) {
842
+ result.data.warnings.forEach((w) => {
843
+ console.log(chalk.yellow(` \u26A0 ${w}`));
844
+ });
845
+ }
846
+ } else {
847
+ console.log(chalk.red("\u2717"), result.message);
848
+ }
849
+ await context.database.disconnect();
850
+ } catch (error) {
851
+ console.error(chalk.red("Error:"), error.message);
852
+ process.exit(1);
853
+ }
854
+ });
855
+ theoryCmd.command("status").description("Show THEORY.MD metadata").action(async () => {
856
+ try {
857
+ const { context } = await initializeSkillContext();
858
+ const { TheorySkill } = await import("../../skills/theory-skill.js");
859
+ const skill = new TheorySkill(context);
860
+ const result = skill.status();
861
+ if (result.success) {
862
+ console.log(chalk.cyan("THEORY.MD Status:"));
863
+ if (result.data?.exists) {
864
+ console.log(` Lines: ${result.data.lineCount}`);
865
+ console.log(
866
+ ` Sections: ${result.data.sections?.length}/${result.data.totalSections}`
867
+ );
868
+ console.log(` Last modified: ${result.data.lastModified}`);
869
+ } else {
870
+ console.log(chalk.gray(" Not found"));
871
+ }
872
+ } else {
873
+ console.log(chalk.red("\u2717"), result.message);
874
+ }
875
+ await context.database.disconnect();
876
+ } catch (error) {
877
+ console.error(chalk.red("Error:"), error.message);
878
+ process.exit(1);
879
+ }
880
+ });
798
881
  skillsCmd.command("help [skill]").description("Show help for a specific skill").action(async (skill) => {
799
882
  if (skill) {
800
883
  switch (skill) {
@@ -846,6 +929,27 @@ Examples:
846
929
 
847
930
  Apply patches: git apply .stackmemory/patches/<file>.patch
848
931
  Review specs: cd /tmp/sm-spec-* && git log --oneline
932
+ `);
933
+ break;
934
+ case "theory":
935
+ console.log(`
936
+ theory \u2014 Living Operating Theory Document
937
+
938
+ Maintains a THEORY.MD at repo root: a narrative capturing your problem
939
+ thesis, mental model, strategy, discoveries, and open questions.
940
+
941
+ Subcommands:
942
+ show Display current THEORY.MD
943
+ init <problem> Create THEORY.MD with scaffold sections
944
+ update <content> Overwrite THEORY.MD (validates, warns on anti-patterns)
945
+ status Show metadata (lines, sections, last modified)
946
+
947
+ Examples:
948
+ stackmemory skills theory init "Build context-aware memory for AI agents"
949
+ stackmemory skills theory show
950
+ stackmemory skills theory status
951
+
952
+ Based on Theorist by @blader (MIT).
849
953
  `);
850
954
  break;
851
955
  default:
@@ -875,7 +979,10 @@ Review specs: cd /tmp/sm-spec-* && git log --oneline
875
979
  );
876
980
  console.log(" linear-run - Execute Linear tasks via RLM orchestrator");
877
981
  console.log(
878
- " agent - Spawn parallel agents (research, maintain, spec-run)\n"
982
+ " agent - Spawn parallel agents (research, maintain, spec-run)"
983
+ );
984
+ console.log(
985
+ " theory - Maintain a living THEORY.MD (show, init, update, status)\n"
879
986
  );
880
987
  console.log(
881
988
  chalk.yellow(
@@ -13,7 +13,9 @@ import chalk from "chalk";
13
13
  import * as os from "os";
14
14
  import * as path from "path";
15
15
  function createStorageTierCommand() {
16
- const cmd = new Command("storage").description("Manage two-tier storage system");
16
+ const cmd = new Command("storage").description(
17
+ "Manage two-tier storage system"
18
+ );
17
19
  cmd.command("status").description("Show storage tier status and statistics").option("--json", "Output as JSON").action(async (options) => {
18
20
  try {
19
21
  const storage = await initializeStorage();
@@ -56,10 +58,18 @@ function createStorageTierCommand() {
56
58
  console.log(table.toString());
57
59
  console.log(`
58
60
  \u{1F4C8} Summary:`);
59
- console.log(` Local Usage: ${chalk.cyan(stats.localUsageMB.toFixed(1))} MB`);
60
- console.log(` Compression Ratio: ${chalk.cyan(stats.compressionRatio.toFixed(2))}x`);
61
- console.log(` Pending Migrations: ${chalk.yellow(stats.migrationsPending)}`);
62
- console.log(` Last Migration: ${stats.lastMigration ? chalk.green(stats.lastMigration.toISOString()) : chalk.gray("Never")}`);
61
+ console.log(
62
+ ` Local Usage: ${chalk.cyan(stats.localUsageMB.toFixed(1))} MB`
63
+ );
64
+ console.log(
65
+ ` Compression Ratio: ${chalk.cyan(stats.compressionRatio.toFixed(2))}x`
66
+ );
67
+ console.log(
68
+ ` Pending Migrations: ${chalk.yellow(stats.migrationsPending)}`
69
+ );
70
+ console.log(
71
+ ` Last Migration: ${stats.lastMigration ? chalk.green(stats.lastMigration.toISOString()) : chalk.gray("Never")}`
72
+ );
63
73
  await storage.shutdown();
64
74
  } catch (error) {
65
75
  logger.error("Failed to get storage status", { error });
@@ -75,7 +85,9 @@ function createStorageTierCommand() {
75
85
  console.log("Would migrate based on current triggers");
76
86
  } else {
77
87
  console.log("\u{1F504} Starting migration process...");
78
- console.log("Migration triggered. Use `storage status` to monitor progress.");
88
+ console.log(
89
+ "Migration triggered. Use `storage status` to monitor progress."
90
+ );
79
91
  }
80
92
  await storage.shutdown();
81
93
  } catch (error) {
@@ -87,7 +99,9 @@ function createStorageTierCommand() {
87
99
  cmd.command("cleanup").description("Clean up old data and optimize storage").option("--force", "Force cleanup without confirmation").option("--tier <tier>", "Clean specific tier only").action(async (options) => {
88
100
  try {
89
101
  if (!options.force) {
90
- console.log("\u26A0\uFE0F This will permanently delete old data. Use --force to confirm.");
102
+ console.log(
103
+ "\u26A0\uFE0F This will permanently delete old data. Use --force to confirm."
104
+ );
91
105
  process.exit(1);
92
106
  }
93
107
  const storage = await initializeStorage();
@@ -172,7 +186,11 @@ function getStorageConfig() {
172
186
  },
173
187
  migration: {
174
188
  ...defaultTwoTierConfig.migration,
175
- offlineQueuePath: path.join(homeDir, ".stackmemory", "offline-queue.json")
189
+ offlineQueuePath: path.join(
190
+ homeDir,
191
+ ".stackmemory",
192
+ "offline-queue.json"
193
+ )
176
194
  }
177
195
  };
178
196
  return config;
@@ -61,8 +61,6 @@ import { filterPending } from "../integrations/mcp/pending-utils.js";
61
61
  import { ProjectManager } from "../core/projects/project-manager.js";
62
62
  import { join } from "path";
63
63
  import { existsSync, mkdirSync } from "fs";
64
- import inquirer from "inquirer";
65
- import { enableChromaDB } from "../core/config/storage-config.js";
66
64
  import { createRequire } from "module";
67
65
  import { fileURLToPath } from "url";
68
66
  import * as pathModule from "path";
@@ -104,10 +102,7 @@ program.name("stackmemory").description(
104
102
  ).version(VERSION);
105
103
  program.command("init").description(
106
104
  "Initialize StackMemory in current project (zero-config by default)"
107
- ).option("-i, --interactive", "Interactive mode with configuration prompts").option(
108
- "--chromadb",
109
- "Enable ChromaDB for semantic search (prompts for API key)"
110
- ).option("--daemon", "Start the background daemon after initialization").action(async (options) => {
105
+ ).option("-i, --interactive", "Interactive mode with configuration prompts").option("--daemon", "Start the background daemon after initialization").action(async (options) => {
111
106
  try {
112
107
  const projectRoot = process.cwd();
113
108
  const dbDir = join(projectRoot, ".stackmemory");
@@ -120,28 +115,6 @@ program.command("init").description(
120
115
  if (!existsSync(dbDir)) {
121
116
  mkdirSync(dbDir, { recursive: true });
122
117
  }
123
- if (options.chromadb) {
124
- await promptAndEnableChromaDB();
125
- } else if (options.interactive && process.stdin.isTTY) {
126
- console.log(chalk.cyan("\nStorage Configuration"));
127
- console.log(
128
- chalk.gray("SQLite (default) is fast and requires no setup.")
129
- );
130
- console.log(
131
- chalk.gray("ChromaDB adds semantic search but requires an API key.\n")
132
- );
133
- const { enableChroma } = await inquirer.prompt([
134
- {
135
- type: "confirm",
136
- name: "enableChroma",
137
- message: "Enable ChromaDB for semantic search?",
138
- default: false
139
- }
140
- ]);
141
- if (enableChroma) {
142
- await promptAndEnableChromaDB();
143
- }
144
- }
145
118
  const dbPath = join(dbDir, "context.db");
146
119
  if (!isTestEnv()) {
147
120
  const db = await openDatabase(dbPath);
@@ -195,35 +168,6 @@ program.command("init").description(
195
168
  process.exit(1);
196
169
  }
197
170
  });
198
- async function promptAndEnableChromaDB() {
199
- const answers = await inquirer.prompt([
200
- {
201
- type: "password",
202
- name: "apiKey",
203
- message: "Enter your ChromaDB API key:",
204
- validate: (input) => {
205
- if (!input || input.trim().length === 0) {
206
- return "API key is required for ChromaDB";
207
- }
208
- return true;
209
- }
210
- },
211
- {
212
- type: "input",
213
- name: "apiUrl",
214
- message: "ChromaDB API URL (press Enter for default):",
215
- default: "https://api.trychroma.com"
216
- }
217
- ]);
218
- enableChromaDB({
219
- apiKey: answers.apiKey,
220
- apiUrl: answers.apiUrl
221
- });
222
- console.log(chalk.green("[OK] ChromaDB enabled for semantic search."));
223
- console.log(
224
- chalk.gray("API key saved to ~/.stackmemory/storage-config.json")
225
- );
226
- }
227
171
  program.command("status").description("Show current StackMemory status").option("--all", "Show all active frames across sessions").option("--project", "Show all active frames in current project").option("--session <id>", "Show frames for specific session").action(async (options) => {
228
172
  return trace.command("stackmemory-status", options, async () => {
229
173
  try {
@@ -11,8 +11,6 @@ function isFeatureEnabled(feature) {
11
11
  switch (feature) {
12
12
  case "linear":
13
13
  return process.env["STACKMEMORY_LINEAR"] !== "false" && (!!process.env["LINEAR_API_KEY"] || !!process.env["LINEAR_OAUTH_TOKEN"]);
14
- case "chromadb":
15
- return process.env["STACKMEMORY_CHROMADB"] === "true";
16
14
  case "aiSummaries":
17
15
  return process.env["STACKMEMORY_AI"] !== "false" && (!!process.env["ANTHROPIC_API_KEY"] || !!process.env["OPENAI_API_KEY"]);
18
16
  case "skills":
@@ -29,7 +27,6 @@ function getFeatureFlags() {
29
27
  return {
30
28
  core: true,
31
29
  linear: isFeatureEnabled("linear"),
32
- chromadb: isFeatureEnabled("chromadb"),
33
30
  aiSummaries: isFeatureEnabled("aiSummaries"),
34
31
  skills: isFeatureEnabled("skills"),
35
32
  ralph: isFeatureEnabled("ralph"),
@@ -46,7 +43,6 @@ function logFeatureStatus() {
46
43
  console.log(
47
44
  ` Linear: ${flags.linear ? "enabled" : "disabled (no API key)"}`
48
45
  );
49
- console.log(` ChromaDB: ${flags.chromadb ? "enabled" : "disabled"}`);
50
46
  console.log(
51
47
  ` AI Summaries: ${flags.aiSummaries ? "enabled" : "disabled (no API key)"}`
52
48
  );
@@ -99,9 +99,16 @@ class DualStackManager {
99
99
  )
100
100
  `;
101
101
  if (this.adapter.isConnected()) {
102
- await this.adapter.execute?.(createStackContextsTable) || this.executeSchemaQuery(createStackContextsTable);
103
- await this.adapter.execute?.(createHandoffRequestsTable) || this.executeSchemaQuery(createHandoffRequestsTable);
104
- await this.adapter.execute?.(createStackSyncLogTable) || this.executeSchemaQuery(createStackSyncLogTable);
102
+ const adapterAny = this.adapter;
103
+ if (!await adapterAny.execute?.(createStackContextsTable)) {
104
+ this.executeSchemaQuery(createStackContextsTable);
105
+ }
106
+ if (!await adapterAny.execute?.(createHandoffRequestsTable)) {
107
+ this.executeSchemaQuery(createHandoffRequestsTable);
108
+ }
109
+ if (!await adapterAny.execute?.(createStackSyncLogTable)) {
110
+ this.executeSchemaQuery(createStackSyncLogTable);
111
+ }
105
112
  }
106
113
  await this.adapter.commitTransaction();
107
114
  logger.info("Dual stack schema initialized successfully");
@@ -56,6 +56,7 @@ class FrameDatabase {
56
56
  created_at INTEGER NOT NULL DEFAULT (unixepoch()),
57
57
  closed_at INTEGER,
58
58
  retention_policy TEXT DEFAULT 'default',
59
+ importance_score REAL DEFAULT 0.5,
59
60
  FOREIGN KEY (parent_frame_id) REFERENCES frames(frame_id)
60
61
  );
61
62
  `);
@@ -90,6 +91,12 @@ class FrameDatabase {
90
91
  );
91
92
  } catch {
92
93
  }
94
+ try {
95
+ this.db.exec(
96
+ "ALTER TABLE frames ADD COLUMN importance_score REAL DEFAULT 0.5"
97
+ );
98
+ } catch {
99
+ }
93
100
  this.db.exec(`
94
101
  CREATE INDEX IF NOT EXISTS idx_frames_run ON frames(run_id);
95
102
  CREATE INDEX IF NOT EXISTS idx_frames_project ON frames(project_id);
@@ -99,6 +106,7 @@ class FrameDatabase {
99
106
  CREATE INDEX IF NOT EXISTS idx_frames_project_state ON frames(project_id, state);
100
107
  CREATE INDEX IF NOT EXISTS idx_frames_project_created ON frames(project_id, created_at DESC);
101
108
  CREATE INDEX IF NOT EXISTS idx_frames_retention_created ON frames(retention_policy, created_at);
109
+ CREATE INDEX IF NOT EXISTS idx_frames_gc_score ON frames(state, retention_policy, importance_score ASC, created_at ASC);
102
110
  CREATE INDEX IF NOT EXISTS idx_events_frame ON events(frame_id);
103
111
  CREATE INDEX IF NOT EXISTS idx_events_seq ON events(frame_id, seq);
104
112
  CREATE INDEX IF NOT EXISTS idx_anchors_frame ON anchors(frame_id);
@@ -142,6 +150,30 @@ class FrameDatabase {
142
150
  -- Set initial schema version if not exists
143
151
  INSERT OR IGNORE INTO schema_version (version) VALUES (1);
144
152
  `);
153
+ this.db.exec(`
154
+ CREATE TABLE IF NOT EXISTS cord_tasks (
155
+ task_id TEXT PRIMARY KEY,
156
+ parent_id TEXT,
157
+ project_id TEXT NOT NULL,
158
+ run_id TEXT NOT NULL,
159
+ goal TEXT NOT NULL,
160
+ prompt TEXT NOT NULL DEFAULT '',
161
+ result TEXT,
162
+ status TEXT NOT NULL DEFAULT 'pending'
163
+ CHECK (status IN ('pending','active','completed','blocked','asked')),
164
+ context_mode TEXT NOT NULL DEFAULT 'spawn'
165
+ CHECK (context_mode IN ('spawn','fork','ask')),
166
+ blocked_by TEXT NOT NULL DEFAULT '[]',
167
+ depth INTEGER NOT NULL DEFAULT 0,
168
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
169
+ completed_at INTEGER,
170
+ FOREIGN KEY (parent_id) REFERENCES cord_tasks(task_id)
171
+ );
172
+ CREATE INDEX IF NOT EXISTS idx_cord_tasks_project ON cord_tasks(project_id);
173
+ CREATE INDEX IF NOT EXISTS idx_cord_tasks_parent ON cord_tasks(parent_id);
174
+ CREATE INDEX IF NOT EXISTS idx_cord_tasks_status ON cord_tasks(status);
175
+ CREATE INDEX IF NOT EXISTS idx_cord_tasks_project_status ON cord_tasks(project_id, status);
176
+ `);
145
177
  logger.info("Frame database schema initialized");
146
178
  } catch (error) {
147
179
  throw new DatabaseError(
@@ -583,12 +583,12 @@ class FrameHandoffManager {
583
583
  ErrorCode.RESOURCE_NOT_FOUND
584
584
  );
585
585
  }
586
- const self = this;
586
+ const activeHandoffs = this.activeHandoffs;
587
587
  return {
588
588
  async *[Symbol.asyncIterator]() {
589
589
  let lastStatus = progress.status;
590
590
  while (lastStatus !== "completed" && lastStatus !== "failed" && lastStatus !== "cancelled") {
591
- const currentProgress = self.activeHandoffs.get(requestId);
591
+ const currentProgress = activeHandoffs.get(requestId);
592
592
  if (currentProgress && currentProgress.status !== lastStatus) {
593
593
  lastStatus = currentProgress.status;
594
594
  yield currentProgress;
@@ -18,7 +18,7 @@ import { FrameDatabase } from "./frame-database.js";
18
18
  import { FrameStack } from "./frame-stack.js";
19
19
  import { FrameDigestGenerator } from "./frame-digest.js";
20
20
  import { FrameRecovery } from "./frame-recovery.js";
21
- class RefactoredFrameManager {
21
+ class FrameManager {
22
22
  frameDb;
23
23
  frameStack;
24
24
  digestGenerator;
@@ -53,7 +53,7 @@ class RefactoredFrameManager {
53
53
  this.frameRecovery = new FrameRecovery(db);
54
54
  this.frameRecovery.setCurrentRunId(this.currentRunId);
55
55
  this.frameDb.initSchema();
56
- logger.info("RefactoredFrameManager initialized", {
56
+ logger.info("FrameManager initialized", {
57
57
  projectId: this.projectId,
58
58
  runId: this.currentRunId,
59
59
  sessionId: this.sessionId
@@ -756,5 +756,5 @@ class RefactoredFrameManager {
756
756
  }
757
757
  }
758
758
  export {
759
- RefactoredFrameManager
759
+ FrameManager
760
760
  };
@@ -2,7 +2,7 @@ import { fileURLToPath as __fileURLToPath } from 'url';
2
2
  import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
- import { RefactoredFrameManager } from "./refactored-frame-manager.js";
5
+ import { FrameManager } from "./frame-manager.js";
6
6
  import { FrameDatabase } from "./frame-database.js";
7
7
  import { FrameStack } from "./frame-stack.js";
8
8
  import { FrameDigestGenerator } from "./frame-digest.js";
@@ -16,7 +16,7 @@ import {
16
16
  export {
17
17
  FrameDatabase,
18
18
  FrameDigestGenerator,
19
- RefactoredFrameManager as FrameManager,
19
+ FrameManager,
20
20
  FrameRecovery,
21
21
  FrameStack,
22
22
  frameLifecycleHooks,