@stackmemoryai/stackmemory 1.2.1 → 1.2.4

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 (100) hide show
  1. package/dist/src/cli/codex-sm.js +6 -8
  2. package/dist/src/cli/commands/config.js +0 -81
  3. package/dist/src/cli/commands/context-rehydrate.js +133 -47
  4. package/dist/src/cli/commands/db.js +35 -8
  5. package/dist/src/cli/commands/handoff.js +1 -1
  6. package/dist/src/cli/commands/linear.js +9 -0
  7. package/dist/src/cli/commands/ralph.js +2 -2
  8. package/dist/src/cli/commands/setup.js +2 -2
  9. package/dist/src/cli/commands/signup.js +3 -1
  10. package/dist/src/cli/commands/skills.js +123 -1
  11. package/dist/src/cli/commands/storage-tier.js +26 -8
  12. package/dist/src/cli/index.js +1 -57
  13. package/dist/src/core/config/feature-flags.js +0 -4
  14. package/dist/src/core/context/dual-stack-manager.js +10 -3
  15. package/dist/src/core/context/frame-database.js +32 -0
  16. package/dist/src/core/context/frame-handoff-manager.js +2 -2
  17. package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
  18. package/dist/src/core/context/index.js +2 -2
  19. package/dist/src/core/database/sqlite-adapter.js +161 -1
  20. package/dist/src/core/digest/frame-digest-integration.js +1 -1
  21. package/dist/src/core/digest/index.js +1 -1
  22. package/dist/src/core/execution/parallel-executor.js +5 -1
  23. package/dist/src/core/projects/project-isolation.js +18 -4
  24. package/dist/src/core/security/index.js +2 -0
  25. package/dist/src/core/security/input-sanitizer.js +23 -0
  26. package/dist/src/core/utils/update-checker.js +10 -6
  27. package/dist/src/daemon/daemon-config.js +2 -1
  28. package/dist/src/daemon/services/auto-save-service.js +121 -0
  29. package/dist/src/daemon/services/maintenance-service.js +76 -1
  30. package/dist/src/features/sweep/prompt-builder.js +2 -2
  31. package/dist/src/hooks/graphiti-hooks.js +149 -0
  32. package/dist/src/hooks/session-summary.js +30 -0
  33. package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +115 -0
  34. package/dist/src/integrations/greptile/client.js +101 -0
  35. package/dist/src/integrations/greptile/config.js +14 -0
  36. package/dist/src/integrations/greptile/index.js +11 -0
  37. package/dist/src/integrations/greptile/types.js +4 -0
  38. package/dist/src/integrations/linear/config.js +3 -1
  39. package/dist/src/integrations/linear/sync.js +18 -5
  40. package/dist/src/integrations/linear/webhook.js +16 -0
  41. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
  42. package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
  43. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +456 -0
  44. package/dist/src/integrations/mcp/handlers/index.js +55 -1
  45. package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
  46. package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
  47. package/dist/src/integrations/mcp/handlers/trace-handlers.js +25 -9
  48. package/dist/src/integrations/mcp/index.js +2 -2
  49. package/dist/src/integrations/mcp/refactored-server.js +31 -10
  50. package/dist/src/integrations/mcp/server.js +27 -0
  51. package/dist/src/integrations/mcp/tool-definitions.js +215 -1
  52. package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
  53. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
  54. package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
  55. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
  56. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
  57. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
  58. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
  59. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
  60. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
  61. package/dist/src/skills/claude-skills.js +46 -103
  62. package/dist/src/skills/parallel-agent-skill.js +514 -0
  63. package/dist/src/utils/hook-installer.js +8 -0
  64. package/package.json +5 -5
  65. package/scripts/gepa/.before-optimize.md +140 -159
  66. package/scripts/gepa/config.json +7 -1
  67. package/scripts/gepa/evals/fixtures/api-endpoint.ts +31 -0
  68. package/scripts/gepa/evals/fixtures/brittle-integration.ts +38 -0
  69. package/scripts/gepa/evals/fixtures/fts5-triggers.sql +23 -0
  70. package/scripts/gepa/evals/fixtures/leaky-service.ts +70 -0
  71. package/scripts/gepa/evals/fixtures/mcp-dispatch-stub.ts +39 -0
  72. package/scripts/gepa/evals/fixtures/pr-diff.txt +24 -0
  73. package/scripts/gepa/evals/fixtures/unsafe-webhook.ts +34 -0
  74. package/scripts/gepa/evals/fixtures/unwrapped-db-op.ts +42 -0
  75. package/scripts/gepa/evals/stackmemory-tasks.jsonl +8 -0
  76. package/scripts/gepa/generations/gen-000/baseline.md +172 -159
  77. package/scripts/gepa/generations/gen-001/baseline.md +172 -159
  78. package/scripts/gepa/generations/gen-001/variant-a.md +156 -146
  79. package/scripts/gepa/generations/gen-001/variant-b.md +199 -170
  80. package/scripts/gepa/generations/gen-001/variant-c.md +127 -46
  81. package/scripts/gepa/generations/gen-001/variant-d.md +160 -107
  82. package/scripts/gepa/hooks/reflect.js +44 -5
  83. package/scripts/gepa/optimize.js +281 -39
  84. package/scripts/gepa/results/eval-1-baseline.json +187 -10
  85. package/scripts/gepa/results/eval-1-variant-a.json +188 -11
  86. package/scripts/gepa/results/eval-1-variant-b.json +188 -11
  87. package/scripts/gepa/results/eval-1-variant-c.json +168 -11
  88. package/scripts/gepa/results/eval-1-variant-d.json +169 -12
  89. package/scripts/gepa/state.json +18 -18
  90. package/scripts/install-claude-hooks-auto.js +8 -0
  91. package/templates/claude-hooks/cord-trace.js +225 -0
  92. package/dist/src/core/config/storage-config.js +0 -114
  93. package/dist/src/core/storage/chromadb-adapter.js +0 -379
  94. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
  95. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
  96. package/dist/src/skills/repo-ingestion-skill.js +0 -631
  97. package/templates/claude-hooks/chromadb-wrapper +0 -21
  98. /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
  99. /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
  100. /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
@@ -0,0 +1,115 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { logger } from "../../core/monitoring/logger.js";
6
+ import { GraphitiClient } from "./client.js";
7
+ class LinearGraphitiBridge {
8
+ client;
9
+ constructor(config = {}) {
10
+ this.client = new GraphitiClient(config);
11
+ }
12
+ async processWebhook(payload) {
13
+ const { action, data } = payload;
14
+ const now = Date.now();
15
+ try {
16
+ const episode = {
17
+ type: `linear_issue_${action}`,
18
+ content: {
19
+ identifier: data.identifier,
20
+ title: data.title,
21
+ action,
22
+ state: data.state?.name,
23
+ priority: data.priority,
24
+ assignee: data.assignee?.name
25
+ },
26
+ timestamp: now,
27
+ source: "linear"
28
+ };
29
+ await this.client.upsertEpisode(episode);
30
+ if (action === "remove") return;
31
+ const entities = [
32
+ {
33
+ type: "Issue",
34
+ name: data.identifier,
35
+ summary: data.title,
36
+ properties: {
37
+ linearId: data.id,
38
+ state: data.state?.name,
39
+ priority: data.priority
40
+ }
41
+ }
42
+ ];
43
+ if (data.assignee) {
44
+ entities.push({
45
+ type: "Person",
46
+ name: data.assignee.name,
47
+ properties: {
48
+ linearId: data.assignee.id,
49
+ email: data.assignee.email
50
+ }
51
+ });
52
+ }
53
+ if (data.team) {
54
+ entities.push({
55
+ type: "Team",
56
+ name: data.team.name,
57
+ properties: { linearId: data.team.id, key: data.team.key }
58
+ });
59
+ }
60
+ if (data.labels?.length) {
61
+ for (const label of data.labels) {
62
+ entities.push({
63
+ type: "Label",
64
+ name: label.name,
65
+ properties: { linearId: label.id, color: label.color }
66
+ });
67
+ }
68
+ }
69
+ const entityResult = await this.client.upsertEntities(entities);
70
+ const issueId = entityResult.ids[0];
71
+ const relations = [];
72
+ let idx = 1;
73
+ if (data.assignee) {
74
+ relations.push({
75
+ fromId: issueId,
76
+ toId: entityResult.ids[idx],
77
+ type: "ASSIGNED_TO",
78
+ validFrom: now
79
+ });
80
+ idx++;
81
+ }
82
+ if (data.team) {
83
+ relations.push({
84
+ fromId: issueId,
85
+ toId: entityResult.ids[idx],
86
+ type: "BELONGS_TO",
87
+ validFrom: now
88
+ });
89
+ idx++;
90
+ }
91
+ if (data.labels?.length) {
92
+ for (let i = 0; i < data.labels.length; i++) {
93
+ relations.push({
94
+ fromId: issueId,
95
+ toId: entityResult.ids[idx + i],
96
+ type: "HAS_LABEL",
97
+ validFrom: now
98
+ });
99
+ }
100
+ }
101
+ if (relations.length > 0) {
102
+ await this.client.upsertRelations(relations);
103
+ }
104
+ } catch (error) {
105
+ logger.debug("Linear-Graphiti bridge error", {
106
+ action,
107
+ identifier: data.identifier,
108
+ error: error instanceof Error ? error.message : String(error)
109
+ });
110
+ }
111
+ }
112
+ }
113
+ export {
114
+ LinearGraphitiBridge
115
+ };
@@ -0,0 +1,101 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7
+ import { DEFAULT_GREPTILE_CONFIG } from "./config.js";
8
+ class GreptileClientError extends Error {
9
+ constructor(message, code) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "GreptileClientError";
13
+ }
14
+ }
15
+ class GreptileClient {
16
+ config;
17
+ client = null;
18
+ transport = null;
19
+ connecting = null;
20
+ constructor(config = {}) {
21
+ this.config = { ...DEFAULT_GREPTILE_CONFIG, ...config };
22
+ if (!this.config.enabled || !this.config.apiKey) {
23
+ throw new GreptileClientError(
24
+ "Greptile integration disabled (GREPTILE_API_KEY not set)",
25
+ "DISABLED"
26
+ );
27
+ }
28
+ }
29
+ async ensureConnected() {
30
+ if (this.client) return this.client;
31
+ if (this.connecting) {
32
+ await this.connecting;
33
+ return this.client;
34
+ }
35
+ this.connecting = this.connect();
36
+ try {
37
+ await this.connecting;
38
+ return this.client;
39
+ } finally {
40
+ this.connecting = null;
41
+ }
42
+ }
43
+ async connect() {
44
+ const transport = new StreamableHTTPClientTransport(
45
+ new URL(this.config.mcpEndpoint),
46
+ {
47
+ requestInit: {
48
+ headers: {
49
+ Authorization: `Bearer ${this.config.apiKey}`
50
+ }
51
+ },
52
+ reconnectionOptions: {
53
+ maxRetries: this.config.maxRetries,
54
+ initialReconnectionDelay: 1e3,
55
+ reconnectionDelayGrowFactor: 1.5,
56
+ maxReconnectionDelay: 1e4
57
+ }
58
+ }
59
+ );
60
+ const client = new Client(
61
+ { name: "stackmemory-greptile", version: "1.0.0" },
62
+ { capabilities: {} }
63
+ );
64
+ transport.onclose = () => {
65
+ this.client = null;
66
+ this.transport = null;
67
+ };
68
+ await client.connect(transport);
69
+ this.client = client;
70
+ this.transport = transport;
71
+ }
72
+ async callTool(name, args = {}) {
73
+ const client = await this.ensureConnected();
74
+ const result = await client.callTool({ name, arguments: args });
75
+ if (result.content && Array.isArray(result.content)) {
76
+ const textParts = result.content.filter(
77
+ (c) => c.type === "text" && typeof c.text === "string"
78
+ ).map((c) => c.text);
79
+ if (textParts.length === 0) return result;
80
+ const combined = textParts.join("\n");
81
+ try {
82
+ return JSON.parse(combined);
83
+ } catch {
84
+ return combined;
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+ async disconnect() {
90
+ if (this.transport) {
91
+ await this.transport.close();
92
+ }
93
+ this.client = null;
94
+ this.transport = null;
95
+ this.connecting = null;
96
+ }
97
+ }
98
+ export {
99
+ GreptileClient,
100
+ GreptileClientError
101
+ };
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ const DEFAULT_GREPTILE_CONFIG = {
6
+ enabled: !!process.env.GREPTILE_API_KEY,
7
+ mcpEndpoint: process.env.GREPTILE_MCP_ENDPOINT || "https://api.greptile.com/mcp",
8
+ apiKey: process.env.GREPTILE_API_KEY || "",
9
+ timeoutMs: 15e3,
10
+ maxRetries: 2
11
+ };
12
+ export {
13
+ DEFAULT_GREPTILE_CONFIG
14
+ };
@@ -0,0 +1,11 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { DEFAULT_GREPTILE_CONFIG } from "./config.js";
6
+ import { GreptileClient, GreptileClientError } from "./client.js";
7
+ export {
8
+ DEFAULT_GREPTILE_CONFIG,
9
+ GreptileClient,
10
+ GreptileClientError
11
+ };
@@ -0,0 +1,4 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
@@ -62,7 +62,9 @@ class LinearConfigManager {
62
62
  * Get default configuration with project isolation
63
63
  */
64
64
  getDefaultConfig() {
65
- const projectId = this.isolationManager.getProjectIdentification(this.projectRoot);
65
+ const projectId = this.isolationManager.getProjectIdentification(
66
+ this.projectRoot
67
+ );
66
68
  return {
67
69
  enabled: true,
68
70
  interval: 5,
@@ -5,7 +5,6 @@ const __dirname = __pathDirname(__filename);
5
5
  import { readFileSync, writeFileSync, existsSync } from "fs";
6
6
  import { join } from "path";
7
7
  import { logger } from "../../core/monitoring/logger.js";
8
- import { IntegrationError, ErrorCode } from "../../core/errors/index.js";
9
8
  import { LinearClient } from "./client.js";
10
9
  class LinearSyncEngine {
11
10
  taskStore;
@@ -15,6 +14,7 @@ class LinearSyncEngine {
15
14
  mappings = /* @__PURE__ */ new Map();
16
15
  projectRoot;
17
16
  mappingsPath;
17
+ _isConfigured = true;
18
18
  constructor(taskStore, authManager, config, projectRoot) {
19
19
  this.taskStore = taskStore;
20
20
  this.authManager = authManager;
@@ -33,10 +33,9 @@ class LinearSyncEngine {
33
33
  } else {
34
34
  const tokens = this.authManager.loadTokens();
35
35
  if (!tokens) {
36
- throw new IntegrationError(
37
- 'Linear API key or authentication tokens not found. Set LINEAR_API_KEY environment variable or run "stackmemory linear setup" first.',
38
- ErrorCode.LINEAR_SYNC_FAILED
39
- );
36
+ logger.info("Linear API key not configured \u2014 skipping Linear sync");
37
+ this._isConfigured = false;
38
+ return;
40
39
  }
41
40
  this.linearClient = new LinearClient({
42
41
  apiKey: tokens.accessToken,
@@ -49,6 +48,12 @@ class LinearSyncEngine {
49
48
  }
50
49
  this.loadMappings();
51
50
  }
51
+ /**
52
+ * Check if Linear credentials are available
53
+ */
54
+ get isConfigured() {
55
+ return this._isConfigured;
56
+ }
52
57
  /**
53
58
  * Update sync configuration
54
59
  */
@@ -59,6 +64,14 @@ class LinearSyncEngine {
59
64
  * Perform bi-directional sync
60
65
  */
61
66
  async sync() {
67
+ if (!this._isConfigured) {
68
+ return {
69
+ success: true,
70
+ synced: { toLinear: 0, fromLinear: 0, updated: 0 },
71
+ conflicts: [],
72
+ errors: []
73
+ };
74
+ }
62
75
  if (!this.config.enabled) {
63
76
  return {
64
77
  success: false,
@@ -22,6 +22,7 @@ function getOptionalEnv(key) {
22
22
  class LinearWebhookHandler {
23
23
  syncEngine;
24
24
  taskStore;
25
+ graphitiBridge;
25
26
  webhookSecret;
26
27
  constructor(webhookSecret) {
27
28
  this.webhookSecret = webhookSecret || process.env["LINEAR_WEBHOOK_SECRET"];
@@ -38,6 +39,12 @@ class LinearWebhookHandler {
38
39
  setTaskStore(taskStore) {
39
40
  this.taskStore = taskStore;
40
41
  }
42
+ /**
43
+ * Set the Graphiti bridge for knowledge graph sync
44
+ */
45
+ setGraphitiBridge(bridge) {
46
+ this.graphitiBridge = bridge;
47
+ }
41
48
  /**
42
49
  * Verify webhook signature
43
50
  */
@@ -104,6 +111,15 @@ class LinearWebhookHandler {
104
111
  default:
105
112
  logger.warn(`Unknown webhook action: ${payload.action}`);
106
113
  }
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
+ }
107
123
  }
108
124
  /**
109
125
  * Handle issue created in Linear
@@ -27,7 +27,12 @@ class CodeExecutionHandler {
27
27
  * Execute code in a controlled environment
28
28
  */
29
29
  async executeCode(params) {
30
- const { language, code, workingDirectory, timeout = EXECUTION_TIMEOUT } = params;
30
+ const {
31
+ language,
32
+ code,
33
+ workingDirectory,
34
+ timeout = EXECUTION_TIMEOUT
35
+ } = params;
31
36
  if (!this.allowedLanguages.includes(language.toLowerCase())) {
32
37
  return {
33
38
  success: false,
@@ -43,7 +48,12 @@ class CodeExecutionHandler {
43
48
  );
44
49
  try {
45
50
  await fs.writeFile(tempFile, code, "utf-8");
46
- const result = await this.runCode(language, tempFile, workingDirectory || this.sandboxDir, timeout);
51
+ const result = await this.runCode(
52
+ language,
53
+ tempFile,
54
+ workingDirectory || this.sandboxDir,
55
+ timeout
56
+ );
47
57
  await fs.unlink(tempFile).catch(() => {
48
58
  });
49
59
  return result;
@@ -98,8 +108,15 @@ class CodeExecutionHandler {
98
108
  clearTimeout(timeoutId);
99
109
  let outputFile;
100
110
  if (truncated) {
101
- outputFile = join(this.sandboxDir, `output_${randomBytes(8).toString("hex")}.txt`);
102
- await fs.writeFile(outputFile, stdout + "\n---STDERR---\n" + stderr, "utf-8").catch(() => {
111
+ outputFile = join(
112
+ this.sandboxDir,
113
+ `output_${randomBytes(8).toString("hex")}.txt`
114
+ );
115
+ await fs.writeFile(
116
+ outputFile,
117
+ stdout + "\n---STDERR---\n" + stderr,
118
+ "utf-8"
119
+ ).catch(() => {
103
120
  });
104
121
  }
105
122
  resolve({
@@ -175,12 +192,21 @@ class CodeExecutionHandler {
175
192
  const warnings = [];
176
193
  const dangerousPatterns = [
177
194
  { pattern: /import\s+os/i, message: "Importing os module detected" },
178
- { pattern: /import\s+subprocess/i, message: "Subprocess module detected" },
195
+ {
196
+ pattern: /import\s+subprocess/i,
197
+ message: "Subprocess module detected"
198
+ },
179
199
  { pattern: /exec\s*\(/i, message: "exec() function detected" },
180
200
  { pattern: /eval\s*\(/i, message: "eval() function detected" },
181
201
  { pattern: /__import__/i, message: "__import__ detected" },
182
- { pattern: /open\s*\([^)]*['"]\//i, message: "Absolute path file access detected" },
183
- { pattern: /require\s*\([^)]*child_process/i, message: "child_process module detected" },
202
+ {
203
+ pattern: /open\s*\([^)]*['"]\//i,
204
+ message: "Absolute path file access detected"
205
+ },
206
+ {
207
+ pattern: /require\s*\([^)]*child_process/i,
208
+ message: "child_process module detected"
209
+ },
184
210
  { pattern: /require\s*\([^)]*fs/i, message: "fs module access detected" }
185
211
  ];
186
212
  for (const { pattern, message } of dangerousPatterns) {