stackmem 1.0.13 → 1.0.15

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/src/cli.ts CHANGED
@@ -282,11 +282,31 @@ async function cmdStart(project: string) {
282
282
 
283
283
  const budgetedMemories = applyContextBudget(scoredMemories, CONTEXT_BUDGET_TOKENS);
284
284
  const omittedCount = scoredMemories.length - budgetedMemories.length;
285
- const tokensUsed = budgetedMemories.reduce(
285
+ let tokensUsed = budgetedMemories.reduce(
286
286
  (sum, memory) => sum + estimateTokens(memory.content),
287
287
  0
288
288
  );
289
289
 
290
+ const { data: fixData, error: fixError } = await supabase
291
+ .from("execution_log")
292
+ .select("*")
293
+ .eq("project", project)
294
+ .eq("resolved", false)
295
+ .order("created_at", { ascending: false })
296
+ .limit(5);
297
+
298
+ if (fixError) fail(`Failed to load execution log: ${fixError.message}`);
299
+
300
+ // Fixes share the same 2000-token budget as memories, which already
301
+ // consumed their portion above — fixes only get whatever room is left.
302
+ const budgetedFixes: { problem: string; solution: string | null }[] = [];
303
+ for (const fix of fixData ?? []) {
304
+ const tokens = estimateTokens(`${fix.problem} ${fix.solution ?? ""}`);
305
+ if (tokensUsed + tokens > CONTEXT_BUDGET_TOKENS) break;
306
+ budgetedFixes.push(fix);
307
+ tokensUsed += tokens;
308
+ }
309
+
290
310
  const memoryIds = budgetedMemories.map((m) => m.id);
291
311
  const linkedIdsByMemory = new Map<string, Set<string>>();
292
312
 
@@ -323,6 +343,16 @@ async function cmdStart(project: string) {
323
343
  console.log("");
324
344
  }
325
345
 
346
+ if (budgetedFixes.length > 0) {
347
+ console.log(`Past Fixes (${budgetedFixes.length})`);
348
+ console.log("-".repeat(14));
349
+ for (const fix of budgetedFixes) {
350
+ console.log(` Problem: ${fix.problem}`);
351
+ console.log(` Solution: ${fix.solution ?? "(pending)"}`);
352
+ console.log("");
353
+ }
354
+ }
355
+
326
356
  console.log(`Context budget: ${tokensUsed}/${CONTEXT_BUDGET_TOKENS} tokens used`);
327
357
  if (omittedCount > 0) {
328
358
  console.log(`${omittedCount} memories omitted (budget exceeded)`);
package/src/index.ts CHANGED
@@ -92,6 +92,23 @@ server.registerTool(
92
92
  linked_memory_ids: Array.from(linkedIdsByMemory.get(m.id) ?? []),
93
93
  }));
94
94
 
95
+ const { data: fixData, error: fixError } = await supabase
96
+ .from("execution_log")
97
+ .select("*")
98
+ .eq("project", project)
99
+ .eq("resolved", false)
100
+ .order("created_at", { ascending: false })
101
+ .limit(5);
102
+
103
+ if (fixError) {
104
+ throw new Error(`Failed to load execution log: ${fixError.message}`);
105
+ }
106
+
107
+ const fixes = (fixData ?? []).map((fix) => ({
108
+ problem: fix.problem,
109
+ solution: fix.solution ?? "pending",
110
+ }));
111
+
95
112
  if (budgetedMemories.length > 0) {
96
113
  const incrementResults = await Promise.all(
97
114
  budgetedMemories.map((memory) =>
@@ -116,6 +133,7 @@ server.registerTool(
116
133
  content: [{ type: "text", text: budgetText }],
117
134
  structuredContent: {
118
135
  memories: memoriesWithLinks,
136
+ fixes,
119
137
  context_budget: {
120
138
  budget_tokens: CONTEXT_BUDGET_TOKENS,
121
139
  tokens_used: tokensUsed,
package/src/scoring.ts CHANGED
@@ -79,6 +79,12 @@ function containsNegationWord(text: string): boolean {
79
79
  return NEGATION_WORDS.some((word) => lower.includes(word));
80
80
  }
81
81
 
82
+ // Hook-generated dependency-change templates (e.g. "removed dependency: X")
83
+ // are structural facts, not decisions — "removed" trips the negation-word
84
+ // check below, but there's nothing for them to supersede/contradict, so
85
+ // they're exempted up front rather than ever entering the negation branch.
86
+ const DEPENDENCY_TEMPLATE_RE = /^(removed|added) dependency:/i;
87
+
82
88
  export type SaveAction =
83
89
  | { action: "duplicate"; matchedId: string }
84
90
  | { action: "superseded"; oldId: string }
@@ -97,7 +103,7 @@ export function decideSaveAction(
97
103
  existingMemories: ExistingMemory[]
98
104
  ): SaveAction {
99
105
  const newEntities = extractEntities(newContent);
100
- const hasNegation = containsNegationWord(newContent);
106
+ const hasNegation = DEPENDENCY_TEMPLATE_RE.test(newContent) ? false : containsNegationWord(newContent);
101
107
 
102
108
  let bestDuplicate: { id: string; score: number } | null = null;
103
109
  let bestSupersede: { id: string; score: number } | null = null;