@stackmemoryai/stackmemory 1.9.0 → 1.10.2

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.
@@ -3,66 +3,31 @@ import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { logger } from "../../../core/monitoring/logger.js";
6
- function _getEnv(key, defaultValue) {
7
- const value = process.env[key];
8
- if (value === void 0) {
9
- if (defaultValue !== void 0) return defaultValue;
10
- throw new Error(`Environment variable ${key} is required`);
11
- }
12
- return value;
13
- }
14
- function _getOptionalEnv(key) {
15
- return process.env[key];
16
- }
17
6
  import {
18
- getSkillStorage,
19
- getDefaultUserId
7
+ getSkillRegistry,
8
+ matchPromptFromRegistry
20
9
  } from "../../../core/skills/index.js";
21
10
  class SkillHandlers {
22
- constructor(redisUrl, userId) {
23
- this.redisUrl = redisUrl;
24
- this.userId = userId || getDefaultUserId();
11
+ constructor(dbPath) {
12
+ this.dbPath = dbPath;
25
13
  }
26
- skillStorage = null;
27
- userId;
28
- /**
29
- * Lazy initialization of skill storage
30
- */
31
- getStorage() {
32
- if (!this.skillStorage) {
33
- const url = this.redisUrl || process.env["REDIS_URL"];
34
- if (!url) {
35
- throw new Error("REDIS_URL not configured for skill storage");
36
- }
37
- this.skillStorage = getSkillStorage({
38
- redisUrl: url,
39
- userId: this.userId
40
- });
14
+ registry = null;
15
+ getRegistry() {
16
+ if (!this.registry) {
17
+ this.registry = getSkillRegistry(this.dbPath);
41
18
  }
42
- return this.skillStorage;
43
- }
44
- /**
45
- * Get current user ID
46
- */
47
- getUserId() {
48
- return this.userId;
19
+ return this.registry;
49
20
  }
50
- /**
51
- * Check if skill storage is available
52
- */
53
21
  isAvailable() {
54
- return !!(this.redisUrl || process.env["REDIS_URL"]);
22
+ return !!(process.env["STACKMEMORY_SKILLS"] || process.env["SM_SKILLS"]);
55
23
  }
56
24
  // ============================================================
57
25
  // SKILL OPERATIONS
58
26
  // ============================================================
59
- /**
60
- * Record a new skill/learning
61
- */
62
27
  async recordSkill(args, context) {
63
28
  try {
64
- const storage = this.getStorage();
65
- const skill = await storage.createSkill({
29
+ const registry = this.getRegistry();
30
+ const skill = registry.createSkill({
66
31
  content: args.content,
67
32
  category: args.category,
68
33
  priority: args.priority || "medium",
@@ -71,10 +36,6 @@ class SkillHandlers {
71
36
  source: args.source || "observation",
72
37
  sessionId: context.sessionId
73
38
  });
74
- logger.info("Recorded skill via MCP", {
75
- skillId: skill.id,
76
- category: skill.category
77
- });
78
39
  return { success: true, skill };
79
40
  } catch (error) {
80
41
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -82,13 +43,10 @@ class SkillHandlers {
82
43
  return { success: false, error: message };
83
44
  }
84
45
  }
85
- /**
86
- * Get relevant skills for current context
87
- */
88
46
  async getRelevantSkills(args) {
89
47
  try {
90
- const storage = this.getStorage();
91
- const skills = await storage.getRelevantSkills({
48
+ const registry = this.getRegistry();
49
+ const skills = registry.getRelevantSkills({
92
50
  tool: args.tool,
93
51
  language: args.language,
94
52
  framework: args.framework,
@@ -102,12 +60,9 @@ class SkillHandlers {
102
60
  return { success: false, error: message };
103
61
  }
104
62
  }
105
- /**
106
- * Query skills with filters
107
- */
108
63
  async querySkills(args) {
109
64
  try {
110
- const storage = this.getStorage();
65
+ const registry = this.getRegistry();
111
66
  const query = {
112
67
  categories: args.categories,
113
68
  priorities: args.priorities,
@@ -119,7 +74,7 @@ class SkillHandlers {
119
74
  sortBy: args.sortBy || "priority",
120
75
  sortOrder: "desc"
121
76
  };
122
- const skills = await storage.querySkills(query);
77
+ const skills = registry.querySkills(query);
123
78
  return { success: true, skills, total: skills.length };
124
79
  } catch (error) {
125
80
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -127,16 +82,11 @@ class SkillHandlers {
127
82
  return { success: false, error: message };
128
83
  }
129
84
  }
130
- /**
131
- * Validate/reinforce a skill
132
- */
133
85
  async validateSkill(args) {
134
86
  try {
135
- const storage = this.getStorage();
136
- const skill = await storage.validateSkill(args.skill_id);
137
- if (!skill) {
138
- return { success: false, error: "Skill not found" };
139
- }
87
+ const registry = this.getRegistry();
88
+ const skill = registry.validateSkill(args.skill_id);
89
+ if (!skill) return { success: false, error: "Skill not found" };
140
90
  return { success: true, skill };
141
91
  } catch (error) {
142
92
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -144,21 +94,16 @@ class SkillHandlers {
144
94
  return { success: false, error: message };
145
95
  }
146
96
  }
147
- /**
148
- * Update a skill
149
- */
150
97
  async updateSkill(args) {
151
98
  try {
152
- const storage = this.getStorage();
153
- const skill = await storage.updateSkill({
99
+ const registry = this.getRegistry();
100
+ const skill = registry.updateSkill({
154
101
  id: args.skill_id,
155
102
  content: args.content,
156
103
  priority: args.priority,
157
104
  tags: args.tags
158
105
  });
159
- if (!skill) {
160
- return { success: false, error: "Skill not found" };
161
- }
106
+ if (!skill) return { success: false, error: "Skill not found" };
162
107
  return { success: true, skill };
163
108
  } catch (error) {
164
109
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -166,16 +111,11 @@ class SkillHandlers {
166
111
  return { success: false, error: message };
167
112
  }
168
113
  }
169
- /**
170
- * Delete a skill
171
- */
172
114
  async deleteSkill(args) {
173
115
  try {
174
- const storage = this.getStorage();
175
- const deleted = await storage.deleteSkill(args.skill_id);
176
- if (!deleted) {
177
- return { success: false, error: "Skill not found" };
178
- }
116
+ const registry = this.getRegistry();
117
+ const deleted = registry.deleteSkill(args.skill_id);
118
+ if (!deleted) return { success: false, error: "Skill not found" };
179
119
  return { success: true };
180
120
  } catch (error) {
181
121
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -184,16 +124,26 @@ class SkillHandlers {
184
124
  }
185
125
  }
186
126
  // ============================================================
127
+ // MATCH PROMPT (NEW)
128
+ // ============================================================
129
+ async matchPrompt(args) {
130
+ try {
131
+ const result = matchPromptFromRegistry(args.prompt);
132
+ return { success: true, result };
133
+ } catch (error) {
134
+ const message = error instanceof Error ? error.message : "Unknown error";
135
+ logger.error("Failed to match prompt", { error: message });
136
+ return { success: false, error: message };
137
+ }
138
+ }
139
+ // ============================================================
187
140
  // SESSION JOURNAL OPERATIONS
188
141
  // ============================================================
189
- /**
190
- * Record a journal entry
191
- */
192
142
  async recordJournalEntry(args, context) {
193
143
  try {
194
- const storage = this.getStorage();
144
+ const registry = this.getRegistry();
195
145
  const sessionId = context.sessionId || "default";
196
- const entry = await storage.createJournalEntry(
146
+ const entry = registry.createJournalEntry(
197
147
  sessionId,
198
148
  args.type,
199
149
  args.title,
@@ -207,14 +157,11 @@ class SkillHandlers {
207
157
  return { success: false, error: message };
208
158
  }
209
159
  }
210
- /**
211
- * Get session journal
212
- */
213
160
  async getSessionJournal(args, context) {
214
161
  try {
215
- const storage = this.getStorage();
162
+ const registry = this.getRegistry();
216
163
  const sessionId = args.session_id || context.sessionId || "default";
217
- const entries = await storage.getSessionJournal(sessionId);
164
+ const entries = registry.getSessionJournal(sessionId);
218
165
  return { success: true, entries };
219
166
  } catch (error) {
220
167
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -222,20 +169,15 @@ class SkillHandlers {
222
169
  return { success: false, error: message };
223
170
  }
224
171
  }
225
- /**
226
- * Promote a journal entry to a skill
227
- */
228
172
  async promoteToSkill(args) {
229
173
  try {
230
- const storage = this.getStorage();
231
- const skill = await storage.promoteToSkill(
174
+ const registry = this.getRegistry();
175
+ const skill = registry.promoteToSkill(
232
176
  args.entry_id,
233
177
  args.category,
234
178
  args.priority || "medium"
235
179
  );
236
- if (!skill) {
237
- return { success: false, error: "Journal entry not found" };
238
- }
180
+ if (!skill) return { success: false, error: "Journal entry not found" };
239
181
  return { success: true, skill };
240
182
  } catch (error) {
241
183
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -246,13 +188,10 @@ class SkillHandlers {
246
188
  // ============================================================
247
189
  // SESSION MANAGEMENT
248
190
  // ============================================================
249
- /**
250
- * Start session tracking
251
- */
252
191
  async startSession(args) {
253
192
  try {
254
- const storage = this.getStorage();
255
- await storage.startSession(args.session_id);
193
+ const registry = this.getRegistry();
194
+ registry.startSession(args.session_id);
256
195
  return { success: true };
257
196
  } catch (error) {
258
197
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -260,16 +199,11 @@ class SkillHandlers {
260
199
  return { success: false, error: message };
261
200
  }
262
201
  }
263
- /**
264
- * End session and get summary
265
- */
266
202
  async endSession(args) {
267
203
  try {
268
- const storage = this.getStorage();
269
- const summary = await storage.endSession(args.session_id);
270
- if (!summary) {
271
- return { success: false, error: "Session not found" };
272
- }
204
+ const registry = this.getRegistry();
205
+ const summary = registry.endSession(args.session_id);
206
+ if (!summary) return { success: false, error: "Session not found" };
273
207
  return { success: true, summary };
274
208
  } catch (error) {
275
209
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -278,60 +212,12 @@ class SkillHandlers {
278
212
  }
279
213
  }
280
214
  // ============================================================
281
- // KNOWLEDGE MANAGEMENT
215
+ // METRICS
282
216
  // ============================================================
283
- /**
284
- * Get promotion candidates
285
- */
286
- async getPromotionCandidates() {
287
- try {
288
- const storage = this.getStorage();
289
- const skills = await storage.getPromotionCandidates();
290
- return { success: true, skills };
291
- } catch (error) {
292
- const message = error instanceof Error ? error.message : "Unknown error";
293
- logger.error("Failed to get promotion candidates", { error: message });
294
- return { success: false, error: message };
295
- }
296
- }
297
- /**
298
- * Promote skill priority
299
- */
300
- async promoteSkillPriority(args) {
301
- try {
302
- const storage = this.getStorage();
303
- const skill = await storage.promoteSkill(args.skill_id);
304
- if (!skill) {
305
- return { success: false, error: "Skill not found" };
306
- }
307
- return { success: true, skill };
308
- } catch (error) {
309
- const message = error instanceof Error ? error.message : "Unknown error";
310
- logger.error("Failed to promote skill priority", { error: message });
311
- return { success: false, error: message };
312
- }
313
- }
314
- /**
315
- * Archive stale skills
316
- */
317
- async archiveStaleSkills(args) {
318
- try {
319
- const storage = this.getStorage();
320
- const count = await storage.archiveStaleSkills(args.days_threshold || 90);
321
- return { success: true, archivedCount: count };
322
- } catch (error) {
323
- const message = error instanceof Error ? error.message : "Unknown error";
324
- logger.error("Failed to archive stale skills", { error: message });
325
- return { success: false, error: message };
326
- }
327
- }
328
- /**
329
- * Get skill storage metrics
330
- */
331
217
  async getSkillMetrics() {
332
218
  try {
333
- const storage = this.getStorage();
334
- const metrics = await storage.getMetrics();
219
+ const registry = this.getRegistry();
220
+ const metrics = registry.getMetrics();
335
221
  return { success: true, metrics };
336
222
  } catch (error) {
337
223
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -520,6 +406,20 @@ const SKILL_TOOL_DEFINITIONS = [
520
406
  type: "object",
521
407
  properties: {}
522
408
  }
409
+ },
410
+ {
411
+ name: "match_prompt",
412
+ description: "Match a prompt against skill rules and return scored skill suggestions",
413
+ inputSchema: {
414
+ type: "object",
415
+ properties: {
416
+ prompt: {
417
+ type: "string",
418
+ description: "The prompt text to match against skill rules"
419
+ }
420
+ },
421
+ required: ["prompt"]
422
+ }
523
423
  }
524
424
  ];
525
425
  export {
@@ -29,10 +29,6 @@ import { execSync } from "child_process";
29
29
  import { FrameManager } from "../../core/context/index.js";
30
30
  import { logger } from "../../core/monitoring/logger.js";
31
31
  import { isFeatureEnabled } from "../../core/config/feature-flags.js";
32
- import {
33
- TaskPriority,
34
- TaskStatus
35
- } from "../../features/tasks/linear-task-manager.js";
36
32
  import { BrowserMCPIntegration } from "../../features/browser/browser-mcp.js";
37
33
  import { TraceDetector } from "../../core/trace/trace-detector.js";
38
34
  import { LLMContextRetrieval } from "../../core/retrieval/index.js";
@@ -3355,8 +3351,6 @@ if (import.meta.url === `file://${process.argv[1]}`) {
3355
3351
  server.start().catch(console.error);
3356
3352
  }
3357
3353
  export {
3358
- TaskPriority,
3359
- TaskStatus,
3360
3354
  server_default as default,
3361
3355
  runMCPServer
3362
3356
  };