@stackmemoryai/stackmemory 1.2.4 → 1.2.7
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 +17 -7
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/commands/ralph.js +3 -3
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/core/extensions/provider-adapter.js +5 -13
- package/dist/src/core/models/model-router.js +3 -5
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/index.js +0 -8
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +3 -0
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +9 -94
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +6 -6
- package/dist/src/skills/claude-skills.js +37 -1
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +8 -0
- package/package.json +21 -8
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- 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/README.md
CHANGED
|
@@ -5,19 +5,26 @@
|
|
|
5
5
|
[](https://codecov.io/gh/stackmemoryai/stackmemory)
|
|
6
6
|
[](https://www.npmjs.com/package/@stackmemoryai/stackmemory)
|
|
7
7
|
|
|
8
|
-
Lossless, project-scoped memory for AI coding tools.
|
|
8
|
+
Lossless, project-scoped memory for AI coding tools. **[Website](https://stackmemoryai.github.io/stackmemory/)** | **[MCP Tools](https://stackmemoryai.github.io/stackmemory/tools.html)** | **[Getting Started](./docs/GETTING_STARTED.md)**
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="site/demo.svg" alt="StackMemory setup demo" width="560">
|
|
12
|
+
</p>
|
|
9
13
|
|
|
10
14
|
StackMemory is a **production-ready memory runtime** for AI coding tools that preserves full project context across sessions:
|
|
11
15
|
|
|
12
16
|
- **Zero-config setup** — `stackmemory init` just works
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
17
|
+
- **32 MCP tools** for Claude Code integration (context, tasks, Linear, traces, discovery, cord, team)
|
|
18
|
+
- **FTS5 full-text search** with BM25 scoring and hybrid retrieval
|
|
19
|
+
- **Full Linear integration** with bidirectional sync and OAuth/API key support
|
|
15
20
|
- **Context persistence** that survives `/clear` operations
|
|
16
21
|
- **Hierarchical frame organization** (nested call stack model)
|
|
22
|
+
- **Multi-wrapper support** — `claude-sm`, `codex-sm`, `opencode-sm` with auto context loading
|
|
17
23
|
- **Skills system** with `/spec` and `/linear-run` for Claude Code
|
|
18
24
|
- **Automatic hooks** for task tracking, Linear sync, and spec progress
|
|
19
25
|
- **Memory monitor daemon** with automatic capture/clear on RAM pressure
|
|
20
|
-
- **
|
|
26
|
+
- **Auto-save service** for periodic context persistence
|
|
27
|
+
- **Comprehensive test coverage** across all core modules
|
|
21
28
|
|
|
22
29
|
Instead of a linear chat log, StackMemory organizes memory as a **call stack** of scoped work (frames), with intelligent LLM-driven retrieval and team collaboration features.
|
|
23
30
|
|
|
@@ -50,13 +57,14 @@ Tools forget decisions and constraints between sessions. StackMemory makes conte
|
|
|
50
57
|
|
|
51
58
|
## Features
|
|
52
59
|
|
|
53
|
-
- **MCP tools** for Claude Code:
|
|
60
|
+
- **MCP tools** for Claude Code: 36 tools across context, tasks, Linear, traces, discovery, cord, and team
|
|
61
|
+
- **FTS5 search**: full-text search with BM25 scoring, hybrid retrieval, and smart thresholds
|
|
54
62
|
- **Skills**: `/spec` (iterative spec generation), `/linear-run` (task execution via RLM)
|
|
55
|
-
- **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates
|
|
63
|
+
- **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates, cord tracing
|
|
56
64
|
- **Prompt Forge**: watches CLAUDE.md and AGENTS.md for prompt optimization (GEPA)
|
|
57
65
|
- **Safe branches**: worktree isolation with `--worktree` or `-w`
|
|
58
66
|
- **Persistent context**: frames, anchors, decisions, retrieval
|
|
59
|
-
- **Integrations**: Linear, DiffMem, Browser MCP
|
|
67
|
+
- **Integrations**: Linear (API key + OAuth), DiffMem, Browser MCP, log-mcp (log analysis)
|
|
60
68
|
|
|
61
69
|
---
|
|
62
70
|
|
|
@@ -347,6 +355,8 @@ See [docs/cli.md](https://github.com/stackmemoryai/stackmemory/blob/main/docs/cl
|
|
|
347
355
|
|
|
348
356
|
## Documentation
|
|
349
357
|
|
|
358
|
+
- [Getting Started](./docs/GETTING_STARTED.md) — Quick start guide (5 minutes)
|
|
359
|
+
- [MCP Tools Reference](https://stackmemoryai.github.io/stackmemory/tools.html) — All 32 MCP tools
|
|
350
360
|
- [CLI Reference](./docs/cli.md) — Full command reference
|
|
351
361
|
- [Setup Guide](./docs/SETUP.md) — Advanced setup options
|
|
352
362
|
- [Development Guide](./docs/DEVELOPMENT.md) — Contributing and development
|
|
@@ -322,6 +322,28 @@ class ClaudeSM {
|
|
|
322
322
|
} catch {
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
getTheoryContent() {
|
|
326
|
+
try {
|
|
327
|
+
let root;
|
|
328
|
+
try {
|
|
329
|
+
root = execSync("git rev-parse --show-toplevel", {
|
|
330
|
+
encoding: "utf-8",
|
|
331
|
+
timeout: 5e3
|
|
332
|
+
}).trim();
|
|
333
|
+
} catch {
|
|
334
|
+
root = process.cwd();
|
|
335
|
+
}
|
|
336
|
+
const theoryPath = path.join(root, "THEORY.MD");
|
|
337
|
+
if (fs.existsSync(theoryPath)) {
|
|
338
|
+
const content = fs.readFileSync(theoryPath, "utf8").trim();
|
|
339
|
+
if (content.length > 0) {
|
|
340
|
+
return content.length > 4e3 ? content.substring(0, 4e3) + "\n\n[...truncated]" : content;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
325
347
|
getHandoffContent() {
|
|
326
348
|
if (!this.config.contextEnabled) return null;
|
|
327
349
|
try {
|
|
@@ -684,13 +706,20 @@ Session ended (exit ${exitCode ?? 0})`));
|
|
|
684
706
|
this.startGEPAWatcher();
|
|
685
707
|
}
|
|
686
708
|
let initialInput = "";
|
|
687
|
-
const
|
|
688
|
-
if (
|
|
689
|
-
const
|
|
690
|
-
if (
|
|
709
|
+
const hasResume = claudeArgs.includes("--continue") || claudeArgs.some((a) => a === "--resume");
|
|
710
|
+
if (!hasResume) {
|
|
711
|
+
const handoffContent = this.getHandoffContent();
|
|
712
|
+
if (handoffContent) {
|
|
691
713
|
initialInput = handoffContent;
|
|
692
714
|
console.log(chalk.gray(" Handoff context ready"));
|
|
693
715
|
}
|
|
716
|
+
const theoryContent = this.getTheoryContent();
|
|
717
|
+
if (theoryContent) {
|
|
718
|
+
initialInput += (initialInput ? "\n\n---\n\n" : "") + `## Operating Theory (THEORY.MD)
|
|
719
|
+
|
|
720
|
+
${theoryContent}`;
|
|
721
|
+
console.log(chalk.gray(" Theory context loaded"));
|
|
722
|
+
}
|
|
694
723
|
}
|
|
695
724
|
console.log();
|
|
696
725
|
if (this.config.useSweep) {
|
|
@@ -6,7 +6,10 @@ const __dirname = __pathDirname(__filename);
|
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
const codexSmPath = path.join(__dirname, "codex-sm.js");
|
|
9
|
-
const args = [
|
|
9
|
+
const args = [
|
|
10
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
11
|
+
...process.argv.slice(2)
|
|
12
|
+
];
|
|
10
13
|
const child = spawn("node", [codexSmPath, ...args], {
|
|
11
14
|
stdio: "inherit",
|
|
12
15
|
env: process.env
|
|
@@ -317,12 +317,12 @@ ${contextResponse.context}`;
|
|
|
317
317
|
"Launch Oracle/Worker pattern swarm for cost-effective execution"
|
|
318
318
|
).argument("<project>", "Project description for Oracle planning").option(
|
|
319
319
|
"--oracle <model>",
|
|
320
|
-
"Oracle model (default: claude-
|
|
321
|
-
"claude-
|
|
320
|
+
"Oracle model (default: claude-sonnet-4-5)",
|
|
321
|
+
"claude-sonnet-4-5-20250929"
|
|
322
322
|
).option(
|
|
323
323
|
"--workers <models>",
|
|
324
324
|
"Comma-separated worker models",
|
|
325
|
-
"claude-
|
|
325
|
+
"claude-haiku-4-5-20251001"
|
|
326
326
|
).option("--budget <amount>", "Cost budget in USD", "10.0").option("--max-workers <count>", "Maximum worker agents", "5").option("--hints <hints>", "Comma-separated planning hints").action(async (project, options) => {
|
|
327
327
|
return trace.command(
|
|
328
328
|
"ralph-oracle-worker",
|
|
@@ -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(
|
|
@@ -113,10 +113,11 @@ class ClaudeAdapter {
|
|
|
113
113
|
}
|
|
114
114
|
async listModels() {
|
|
115
115
|
return [
|
|
116
|
-
"claude-opus-4-
|
|
116
|
+
"claude-opus-4-6",
|
|
117
|
+
"claude-sonnet-4-5-20250929",
|
|
118
|
+
"claude-haiku-4-5-20251001",
|
|
117
119
|
"claude-sonnet-4-20250514",
|
|
118
|
-
"claude-3-5-haiku-20241022"
|
|
119
|
-
"claude-3-opus-20240229"
|
|
120
|
+
"claude-3-5-haiku-20241022"
|
|
120
121
|
];
|
|
121
122
|
}
|
|
122
123
|
buildRequestBody(messages, options) {
|
|
@@ -360,16 +361,7 @@ class GPTAdapter {
|
|
|
360
361
|
}
|
|
361
362
|
}
|
|
362
363
|
async listModels() {
|
|
363
|
-
return [
|
|
364
|
-
"gpt-4o",
|
|
365
|
-
"gpt-4o-mini",
|
|
366
|
-
"gpt-4-turbo",
|
|
367
|
-
"gpt-4",
|
|
368
|
-
"gpt-3.5-turbo",
|
|
369
|
-
"o1",
|
|
370
|
-
"o1-mini",
|
|
371
|
-
"o1-preview"
|
|
372
|
-
];
|
|
364
|
+
return ["gpt-4o", "gpt-4o-mini", "o3-mini", "o4-mini"];
|
|
373
365
|
}
|
|
374
366
|
}
|
|
375
367
|
function createProvider(id, config) {
|
|
@@ -22,16 +22,14 @@ const MODEL_TOKEN_LIMITS = {
|
|
|
22
22
|
"claude-sonnet-4-5-20250929": 2e5,
|
|
23
23
|
"claude-haiku-4-5-20251001": 2e5,
|
|
24
24
|
"claude-sonnet-4-20250514": 2e5,
|
|
25
|
-
// Claude 3.x
|
|
25
|
+
// Claude 3.x (legacy, still functional)
|
|
26
26
|
"claude-3-5-sonnet-20241022": 2e5,
|
|
27
27
|
"claude-3-5-haiku-20241022": 2e5,
|
|
28
|
-
"claude-3-opus-20240229": 2e5,
|
|
29
28
|
// OpenAI
|
|
30
29
|
"gpt-4o": 128e3,
|
|
31
|
-
"gpt-
|
|
32
|
-
"gpt-4": 8192,
|
|
33
|
-
o1: 2e5,
|
|
30
|
+
"gpt-4o-mini": 128e3,
|
|
34
31
|
"o3-mini": 2e5,
|
|
32
|
+
"o4-mini": 2e5,
|
|
35
33
|
// Qwen
|
|
36
34
|
"qwen3-max-2025-01-23": 128e3,
|
|
37
35
|
// Cerebras
|
package/dist/src/hooks/daemon.js
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import { join, extname, relative } from "path";
|
|
14
14
|
import { spawn } from "child_process";
|
|
15
15
|
import { loadConfig } from "./config.js";
|
|
16
|
-
import { GraphitiHooks } from "./graphiti-hooks.js";
|
|
17
16
|
import {
|
|
18
17
|
hookEmitter
|
|
19
18
|
} from "./events.js";
|
|
@@ -143,13 +142,6 @@ function registerBuiltinHandlers() {
|
|
|
143
142
|
hookEmitter.registerHandler("file_change", handleFileChange);
|
|
144
143
|
hookEmitter.registerHandler("suggestion_ready", handleSuggestionReady);
|
|
145
144
|
hookEmitter.registerHandler("error", handleError);
|
|
146
|
-
try {
|
|
147
|
-
if (process.env.GRAPHITI_ENDPOINT || process.env.GRAPHITI_ENABLED === "true") {
|
|
148
|
-
const graphiti = new GraphitiHooks();
|
|
149
|
-
graphiti.register(hookEmitter);
|
|
150
|
-
}
|
|
151
|
-
} catch {
|
|
152
|
-
}
|
|
153
145
|
hookEmitter.on("*", () => {
|
|
154
146
|
state.eventsProcessed++;
|
|
155
147
|
});
|
|
@@ -22,7 +22,6 @@ function getOptionalEnv(key) {
|
|
|
22
22
|
class LinearWebhookHandler {
|
|
23
23
|
syncEngine;
|
|
24
24
|
taskStore;
|
|
25
|
-
graphitiBridge;
|
|
26
25
|
webhookSecret;
|
|
27
26
|
constructor(webhookSecret) {
|
|
28
27
|
this.webhookSecret = webhookSecret || process.env["LINEAR_WEBHOOK_SECRET"];
|
|
@@ -39,12 +38,6 @@ class LinearWebhookHandler {
|
|
|
39
38
|
setTaskStore(taskStore) {
|
|
40
39
|
this.taskStore = taskStore;
|
|
41
40
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Set the Graphiti bridge for knowledge graph sync
|
|
44
|
-
*/
|
|
45
|
-
setGraphitiBridge(bridge) {
|
|
46
|
-
this.graphitiBridge = bridge;
|
|
47
|
-
}
|
|
48
41
|
/**
|
|
49
42
|
* Verify webhook signature
|
|
50
43
|
*/
|
|
@@ -111,15 +104,6 @@ class LinearWebhookHandler {
|
|
|
111
104
|
default:
|
|
112
105
|
logger.warn(`Unknown webhook action: ${payload.action}`);
|
|
113
106
|
}
|
|
114
|
-
if (this.graphitiBridge) {
|
|
115
|
-
this.graphitiBridge.processWebhook(payload).catch((err) => {
|
|
116
|
-
logger.debug("Linear-Graphiti bridge error", {
|
|
117
|
-
action: payload.action,
|
|
118
|
-
identifier: payload.data.identifier,
|
|
119
|
-
error: err instanceof Error ? err.message : String(err)
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
107
|
}
|
|
124
108
|
/**
|
|
125
109
|
* Handle issue created in Linear
|
|
@@ -99,10 +99,6 @@ class MCPHandlerFactory {
|
|
|
99
99
|
return this.taskHandlers.handleGetActiveTasks.bind(this.taskHandlers);
|
|
100
100
|
case "get_task_metrics":
|
|
101
101
|
return this.taskHandlers.handleGetTaskMetrics.bind(this.taskHandlers);
|
|
102
|
-
case "add_task_dependency":
|
|
103
|
-
return this.taskHandlers.handleAddTaskDependency.bind(
|
|
104
|
-
this.taskHandlers
|
|
105
|
-
);
|
|
106
102
|
// Linear handlers
|
|
107
103
|
case "linear_sync":
|
|
108
104
|
return this.linearHandlers.handleLinearSync.bind(this.linearHandlers);
|
|
@@ -119,8 +115,6 @@ class MCPHandlerFactory {
|
|
|
119
115
|
// Trace handlers
|
|
120
116
|
case "get_traces":
|
|
121
117
|
return this.traceHandlers.handleGetTraces.bind(this.traceHandlers);
|
|
122
|
-
case "analyze_traces":
|
|
123
|
-
return this.traceHandlers.handleAnalyzeTraces.bind(this.traceHandlers);
|
|
124
118
|
case "start_browser_debug":
|
|
125
119
|
return this.traceHandlers.handleStartBrowserDebug.bind(
|
|
126
120
|
this.traceHandlers
|
|
@@ -193,7 +187,6 @@ class MCPHandlerFactory {
|
|
|
193
187
|
"update_task_status",
|
|
194
188
|
"get_active_tasks",
|
|
195
189
|
"get_task_metrics",
|
|
196
|
-
"add_task_dependency",
|
|
197
190
|
// Linear tools
|
|
198
191
|
"linear_sync",
|
|
199
192
|
"linear_update_task",
|
|
@@ -201,7 +194,6 @@ class MCPHandlerFactory {
|
|
|
201
194
|
"linear_status",
|
|
202
195
|
// Trace tools
|
|
203
196
|
"get_traces",
|
|
204
|
-
"analyze_traces",
|
|
205
197
|
"start_browser_debug",
|
|
206
198
|
"take_screenshot",
|
|
207
199
|
"execute_script",
|
|
@@ -31,7 +31,6 @@ import { LLMContextRetrieval } from "../../core/retrieval/index.js";
|
|
|
31
31
|
import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
|
|
32
32
|
import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
|
|
33
33
|
import { GreptileHandlers } from "./handlers/greptile-handlers.js";
|
|
34
|
-
import { GraphitiClient } from "../graphiti/client.js";
|
|
35
34
|
import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
|
|
36
35
|
import { v4 as uuidv4 } from "uuid";
|
|
37
36
|
import {
|
|
@@ -67,7 +66,6 @@ class LocalStackMemoryMCP {
|
|
|
67
66
|
diffMemHandlers;
|
|
68
67
|
greptileHandlers;
|
|
69
68
|
providerHandlers = null;
|
|
70
|
-
graphitiClient = null;
|
|
71
69
|
pendingPlans = /* @__PURE__ */ new Map();
|
|
72
70
|
constructor() {
|
|
73
71
|
this.projectRoot = this.findProjectRoot();
|
|
@@ -142,15 +140,6 @@ class LocalStackMemoryMCP {
|
|
|
142
140
|
this.diffMemHandlers = new DiffMemHandlers();
|
|
143
141
|
this.greptileHandlers = new GreptileHandlers();
|
|
144
142
|
this.initProviderHandlers();
|
|
145
|
-
if (process.env.GRAPHITI_ENDPOINT) {
|
|
146
|
-
this.graphitiClient = new GraphitiClient({
|
|
147
|
-
endpoint: process.env.GRAPHITI_ENDPOINT,
|
|
148
|
-
projectNamespace: process.env.STACKMEMORY_PROJECT_ID || this.projectId
|
|
149
|
-
});
|
|
150
|
-
logger.info("Graphiti client initialized", {
|
|
151
|
-
endpoint: process.env.GRAPHITI_ENDPOINT
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
143
|
this.setupHandlers();
|
|
155
144
|
this.loadInitialContext();
|
|
156
145
|
this.loadPendingPlans();
|
|
@@ -711,24 +700,6 @@ ${summary}...`, 0.8);
|
|
|
711
700
|
properties: {}
|
|
712
701
|
}
|
|
713
702
|
},
|
|
714
|
-
{
|
|
715
|
-
name: "add_task_dependency",
|
|
716
|
-
description: "Add dependency relationship between tasks",
|
|
717
|
-
inputSchema: {
|
|
718
|
-
type: "object",
|
|
719
|
-
properties: {
|
|
720
|
-
taskId: {
|
|
721
|
-
type: "string",
|
|
722
|
-
description: "Task that depends on another"
|
|
723
|
-
},
|
|
724
|
-
dependsOnId: {
|
|
725
|
-
type: "string",
|
|
726
|
-
description: "Task ID that this depends on"
|
|
727
|
-
}
|
|
728
|
-
},
|
|
729
|
-
required: ["taskId", "dependsOnId"]
|
|
730
|
-
}
|
|
731
|
-
},
|
|
732
703
|
{
|
|
733
704
|
name: "linear_sync",
|
|
734
705
|
description: "Sync tasks with Linear",
|
|
@@ -1087,51 +1058,6 @@ ${summary}...`, 0.8);
|
|
|
1087
1058
|
properties: {}
|
|
1088
1059
|
}
|
|
1089
1060
|
},
|
|
1090
|
-
// Graphiti tools (only active when GRAPHITI_ENDPOINT is set)
|
|
1091
|
-
...this.graphitiClient ? [
|
|
1092
|
-
{
|
|
1093
|
-
name: "graphiti_status",
|
|
1094
|
-
description: "Check Graphiti temporal knowledge graph connection status",
|
|
1095
|
-
inputSchema: {
|
|
1096
|
-
type: "object",
|
|
1097
|
-
properties: {}
|
|
1098
|
-
}
|
|
1099
|
-
},
|
|
1100
|
-
{
|
|
1101
|
-
name: "graphiti_query",
|
|
1102
|
-
description: "Query the Graphiti temporal knowledge graph for entities, relations, and episodes",
|
|
1103
|
-
inputSchema: {
|
|
1104
|
-
type: "object",
|
|
1105
|
-
properties: {
|
|
1106
|
-
query: {
|
|
1107
|
-
type: "string",
|
|
1108
|
-
description: "Semantic text query"
|
|
1109
|
-
},
|
|
1110
|
-
entityTypes: {
|
|
1111
|
-
type: "array",
|
|
1112
|
-
items: { type: "string" },
|
|
1113
|
-
description: 'Entity types to filter (e.g., ["Person", "File", "Issue"])'
|
|
1114
|
-
},
|
|
1115
|
-
validFrom: {
|
|
1116
|
-
type: "number",
|
|
1117
|
-
description: "Start of time window (epoch ms)"
|
|
1118
|
-
},
|
|
1119
|
-
validTo: {
|
|
1120
|
-
type: "number",
|
|
1121
|
-
description: "End of time window (epoch ms)"
|
|
1122
|
-
},
|
|
1123
|
-
maxHops: {
|
|
1124
|
-
type: "number",
|
|
1125
|
-
description: "Graph traversal depth (default 2)"
|
|
1126
|
-
},
|
|
1127
|
-
k: {
|
|
1128
|
-
type: "number",
|
|
1129
|
-
description: "Top-k results (default 20)"
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
] : [],
|
|
1135
1061
|
// Greptile tools (only active when GREPTILE_API_KEY is set)
|
|
1136
1062
|
...process.env.GREPTILE_API_KEY ? this.greptileHandlers.getToolDefinitions() : [],
|
|
1137
1063
|
// Provider tools (only active when STACKMEMORY_MULTI_PROVIDER=true)
|
|
@@ -1299,9 +1225,6 @@ ${summary}...`, 0.8);
|
|
|
1299
1225
|
case "get_task_metrics":
|
|
1300
1226
|
result = await this.handleGetTaskMetrics(args);
|
|
1301
1227
|
break;
|
|
1302
|
-
case "add_task_dependency":
|
|
1303
|
-
result = await this.handleAddTaskDependency(args);
|
|
1304
|
-
break;
|
|
1305
1228
|
case "linear_sync":
|
|
1306
1229
|
result = await this.handleLinearSync(args);
|
|
1307
1230
|
break;
|
|
@@ -1456,59 +1379,6 @@ ${summary}...`, 0.8);
|
|
|
1456
1379
|
);
|
|
1457
1380
|
}
|
|
1458
1381
|
break;
|
|
1459
|
-
// Graphiti tools
|
|
1460
|
-
case "graphiti_status":
|
|
1461
|
-
if (!this.graphitiClient) {
|
|
1462
|
-
result = {
|
|
1463
|
-
content: [
|
|
1464
|
-
{
|
|
1465
|
-
type: "text",
|
|
1466
|
-
text: JSON.stringify({
|
|
1467
|
-
connected: false,
|
|
1468
|
-
message: "Graphiti integration disabled (GRAPHITI_ENDPOINT not set)"
|
|
1469
|
-
})
|
|
1470
|
-
}
|
|
1471
|
-
]
|
|
1472
|
-
};
|
|
1473
|
-
} else {
|
|
1474
|
-
const status = await this.graphitiClient.getStatus();
|
|
1475
|
-
result = {
|
|
1476
|
-
content: [
|
|
1477
|
-
{ type: "text", text: JSON.stringify(status, null, 2) }
|
|
1478
|
-
]
|
|
1479
|
-
};
|
|
1480
|
-
}
|
|
1481
|
-
break;
|
|
1482
|
-
case "graphiti_query":
|
|
1483
|
-
if (!this.graphitiClient) {
|
|
1484
|
-
result = {
|
|
1485
|
-
content: [
|
|
1486
|
-
{
|
|
1487
|
-
type: "text",
|
|
1488
|
-
text: "Graphiti integration disabled (GRAPHITI_ENDPOINT not set)"
|
|
1489
|
-
}
|
|
1490
|
-
]
|
|
1491
|
-
};
|
|
1492
|
-
} else {
|
|
1493
|
-
const gCtx = await this.graphitiClient.queryTemporal({
|
|
1494
|
-
query: args.query,
|
|
1495
|
-
entityTypes: args.entityTypes,
|
|
1496
|
-
validFrom: args.validFrom,
|
|
1497
|
-
validTo: args.validTo,
|
|
1498
|
-
maxHops: args.maxHops,
|
|
1499
|
-
k: args.k
|
|
1500
|
-
});
|
|
1501
|
-
const text = gCtx.chunks.map((c) => c.text).join("\n\n");
|
|
1502
|
-
result = {
|
|
1503
|
-
content: [
|
|
1504
|
-
{
|
|
1505
|
-
type: "text",
|
|
1506
|
-
text: text || `No results found (${gCtx.totalTokens} tokens searched)`
|
|
1507
|
-
}
|
|
1508
|
-
]
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
break;
|
|
1512
1382
|
default:
|
|
1513
1383
|
throw new Error(`Unknown tool: ${name}`);
|
|
1514
1384
|
}
|