@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.
- package/README.md +10 -6
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -9
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +196 -67
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +37 -103
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +16 -0
- package/package.json +6 -6
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/graphiti/client.js +0 -115
- package/dist/src/integrations/graphiti/config.js +0 -17
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
- package/dist/src/integrations/graphiti/types.js +0 -4
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /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 {
|
|
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(
|
|
43
|
-
|
|
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
|
-
{
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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/
|
|
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
|
|
180
|
-
|
|
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
|
-
|
|
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(
|
|
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)
|
|
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(
|
|
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(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
189
|
+
offlineQueuePath: path.join(
|
|
190
|
+
homeDir,
|
|
191
|
+
".stackmemory",
|
|
192
|
+
"offline-queue.json"
|
|
193
|
+
)
|
|
176
194
|
}
|
|
177
195
|
};
|
|
178
196
|
return config;
|
package/dist/src/cli/index.js
CHANGED
|
@@ -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
|
-
|
|
103
|
-
await
|
|
104
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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("
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
19
|
+
FrameManager,
|
|
20
20
|
FrameRecovery,
|
|
21
21
|
FrameStack,
|
|
22
22
|
frameLifecycleHooks,
|