memorix 0.9.15 → 0.9.16

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.9.16] — 2026-02-26
6
+
7
+ ### Architecture
8
+ - **Classify → Policy → Store pipeline** — Replaced the monolithic `switch/case` handler (527 lines) with a clean declarative pipeline (432 lines). Inspired by claude-mem's store-first philosophy and mcp-memory-service's configurable scoring.
9
+ - **Tool Taxonomy** — `classifyTool()` categorizes tools into `file_modify | file_read | command | search | memorix_internal | unknown`. Each category has a declarative `StoragePolicy` (store mode, minLength, defaultType).
10
+ - **Pattern detection = classification only** — Pattern detection now only determines observation *type* (decision, error, etc.), not whether to store. Storage decisions are made by policy.
11
+ - **Unified `TYPE_EMOJI`** — Single exported constant, eliminating 3 duplicated copies across handler and session_start.
12
+
13
+ ### Fixed
14
+ - **🔴 Critical: Bash commands with `cd` prefix silently dropped** — Claude Code sends Bash commands as `cd /project && npm test 2>&1`. The noise filter `/^cd\b/` matched the `cd` prefix and silently discarded the entire command. This caused `npm test`, `npm install express`, `node -e "..."`, and all other project-scoped commands to never be stored. Fix: `extractRealCommand()` strips `cd path && ` prefix before noise checking, so `cd /path && npm test` is correctly evaluated as `npm test`.
15
+ - **Cooldown key too broad** — Old key `post_tool:Bash` meant ALL Bash commands shared one 30-second cooldown. New key uses `event:filePath|command|toolName`, so `npm test` and `npm install` have independent cooldowns.
16
+ - **Store-first for commands** — Command-category tools now use `store: 'always'` policy with minLength 30 (down from 50-200), capturing more meaningful development activity.
17
+
5
18
  ## [0.9.15] — 2026-02-26
6
19
 
7
20
  ### Fixed
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <a href="https://www.npmjs.com/package/memorix"><img src="https://img.shields.io/npm/dm/memorix.svg?style=flat-square&color=blue" alt="npm downloads"></a>
9
9
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green.svg?style=flat-square" alt="License"></a>
10
10
  <a href="https://github.com/AVIDS2/memorix"><img src="https://img.shields.io/github/stars/AVIDS2/memorix?style=flat-square&color=yellow" alt="GitHub stars"></a>
11
- <img src="https://img.shields.io/badge/tests-505%20passed-brightgreen?style=flat-square" alt="Tests">
11
+ <img src="https://img.shields.io/badge/tests-507%20passed-brightgreen?style=flat-square" alt="Tests">
12
12
  </p>
13
13
  <p align="center">
14
14
  <img src="https://img.shields.io/badge/Works%20with-Cursor-orange?style=flat-square" alt="Cursor">
package/README.zh-CN.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <a href="https://www.npmjs.com/package/memorix"><img src="https://img.shields.io/npm/dm/memorix.svg?style=flat-square&color=blue" alt="npm downloads"></a>
9
9
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green.svg?style=flat-square" alt="License"></a>
10
10
  <a href="https://github.com/AVIDS2/memorix"><img src="https://img.shields.io/github/stars/AVIDS2/memorix?style=flat-square&color=yellow" alt="GitHub stars"></a>
11
- <img src="https://img.shields.io/badge/tests-505%20passed-brightgreen?style=flat-square" alt="Tests">
11
+ <img src="https://img.shields.io/badge/tests-507%20passed-brightgreen?style=flat-square" alt="Tests">
12
12
  </p>
13
13
  <p align="center">
14
14
  <img src="https://img.shields.io/badge/Works%20with-Cursor-orange?style=flat-square" alt="Cursor">
package/dist/cli/index.js CHANGED
@@ -39920,7 +39920,7 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
39920
39920
  lines.push("");
39921
39921
  }
39922
39922
  const PRIORITY_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "problem-solution", "trade-off", "discovery"]);
39923
- const TYPE_EMOJI = {
39923
+ const TYPE_EMOJI2 = {
39924
39924
  "gotcha": "\u{1F534}",
39925
39925
  "decision": "\u{1F7E4}",
39926
39926
  "problem-solution": "\u{1F7E1}",
@@ -39935,7 +39935,7 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
39935
39935
  if (priorityObs.length > 0) {
39936
39936
  lines.push(`## Key Memories`);
39937
39937
  for (const obs of priorityObs) {
39938
- const emoji2 = TYPE_EMOJI[obs.type] ?? "\u{1F4CC}";
39938
+ const emoji2 = TYPE_EMOJI2[obs.type] ?? "\u{1F4CC}";
39939
39939
  const fact = obs.facts?.[0] ? ` \u2014 ${obs.facts[0]}` : "";
39940
39940
  lines.push(`${emoji2} ${obs.title}${fact}`);
39941
39941
  }
@@ -43746,10 +43746,37 @@ var init_pattern_detector = __esm({
43746
43746
  // src/hooks/handler.ts
43747
43747
  var handler_exports = {};
43748
43748
  __export(handler_exports, {
43749
+ TYPE_EMOJI: () => TYPE_EMOJI,
43749
43750
  handleHookEvent: () => handleHookEvent,
43750
43751
  resetCooldowns: () => resetCooldowns,
43751
43752
  runHook: () => runHook
43752
43753
  });
43754
+ function classifyTool(input) {
43755
+ if (input.event === "post_edit") return "file_modify";
43756
+ if (input.event === "post_command") return "command";
43757
+ const name = (input.toolName ?? "").toLowerCase();
43758
+ if (name.startsWith("memorix_")) return "memorix_internal";
43759
+ if (/^(write|edit|multi_?edit|multiedittool|create|patch|insert|notebook_?edit)$/i.test(name)) {
43760
+ return "file_modify";
43761
+ }
43762
+ if (/^(read|read_?file|view|list_?dir)$/i.test(name)) {
43763
+ return "file_read";
43764
+ }
43765
+ if (/^(bash|shell|terminal|command|run)$/i.test(name) || input.command) {
43766
+ return "command";
43767
+ }
43768
+ if (/^(search|grep|ripgrep|find_?by_?name|glob)$/i.test(name)) {
43769
+ return "search";
43770
+ }
43771
+ return "unknown";
43772
+ }
43773
+ function extractRealCommand(command) {
43774
+ return command.replace(/^cd\s+\S+\s*&&\s*/i, "").trim();
43775
+ }
43776
+ function isNoiseCommand(command) {
43777
+ const real = extractRealCommand(command);
43778
+ return NOISE_COMMANDS.some((r4) => r4.test(real));
43779
+ }
43753
43780
  function isInCooldown(eventKey) {
43754
43781
  const last = cooldowns.get(eventKey);
43755
43782
  if (!last) return false;
@@ -43766,7 +43793,7 @@ function extractContent(input) {
43766
43793
  if (input.userPrompt) parts.push(input.userPrompt);
43767
43794
  if (input.aiResponse) parts.push(input.aiResponse);
43768
43795
  if (input.commandOutput) parts.push(input.commandOutput);
43769
- if (input.command) parts.push(`Command: ${input.command}`);
43796
+ if (input.command) parts.push(`Command: ${extractRealCommand(input.command)}`);
43770
43797
  if (input.filePath) parts.push(`File: ${input.filePath}`);
43771
43798
  if (input.edits) {
43772
43799
  for (const edit of input.edits) {
@@ -43782,8 +43809,7 @@ function extractContent(input) {
43782
43809
  parts.push(`File: ${input.toolInput.file_path}`);
43783
43810
  }
43784
43811
  if (input.toolInput.content) {
43785
- const content = input.toolInput.content;
43786
- parts.push(content.slice(0, 1e3));
43812
+ parts.push(input.toolInput.content.slice(0, 1e3));
43787
43813
  }
43788
43814
  if (input.toolInput.old_string || input.toolInput.new_string) {
43789
43815
  const oldStr = input.toolInput.old_string ?? "";
@@ -43804,7 +43830,7 @@ function deriveEntityName(input) {
43804
43830
  }
43805
43831
  if (input.toolName) return input.toolName;
43806
43832
  if (input.command) {
43807
- const firstWord = input.command.split(/\s+/)[0];
43833
+ const firstWord = extractRealCommand(input.command).split(/\s+/)[0];
43808
43834
  return firstWord.replace(/[^a-zA-Z0-9-_]/g, "");
43809
43835
  }
43810
43836
  return "session";
@@ -43817,16 +43843,17 @@ function generateTitle(input, patternType) {
43817
43843
  return `${verb} ${filename}`.slice(0, maxLen);
43818
43844
  }
43819
43845
  if (input.command) {
43820
- return `Ran: ${input.command}`.slice(0, maxLen);
43846
+ return `Ran: ${extractRealCommand(input.command)}`.slice(0, maxLen);
43821
43847
  }
43822
43848
  if (input.userPrompt) {
43823
43849
  return input.userPrompt.slice(0, maxLen);
43824
43850
  }
43825
43851
  return `Session activity (${patternType})`;
43826
43852
  }
43827
- function buildObservation(input, content) {
43853
+ function buildObservation(input, content, category) {
43828
43854
  const pattern = detectBestPattern(content);
43829
- const fallbackType = input.filePath ? "what-changed" : "discovery";
43855
+ const policy = STORAGE_POLICY[category] ?? STORAGE_POLICY.unknown;
43856
+ const fallbackType = input.filePath ? "what-changed" : policy.defaultType;
43830
43857
  const obsType = pattern ? patternToObservationType(pattern.type) : fallbackType;
43831
43858
  return {
43832
43859
  entityName: deriveEntityName(input),
@@ -43837,212 +43864,111 @@ function buildObservation(input, content) {
43837
43864
  `Agent: ${input.agent}`,
43838
43865
  `Session: ${input.sessionId}`,
43839
43866
  ...input.filePath ? [`File: ${input.filePath}`] : [],
43840
- ...input.command ? [`Command: ${input.command}`] : []
43867
+ ...input.command ? [`Command: ${extractRealCommand(input.command)}`] : []
43841
43868
  ],
43842
43869
  concepts: pattern?.matchedKeywords ?? [],
43843
43870
  filesModified: input.filePath ? [input.filePath] : []
43844
43871
  };
43845
43872
  }
43846
- async function handleHookEvent(input) {
43847
- const defaultOutput = { continue: true };
43848
- if (input.toolName === "memorix_store" || input.toolName === "memorix_search") {
43849
- return { observation: null, output: defaultOutput };
43850
- }
43851
- switch (input.event) {
43852
- case "session_start": {
43853
- let contextSummary = "";
43854
- try {
43855
- const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
43856
- const { getProjectDataDir: getProjectDataDir2, loadObservationsJson: loadObservationsJson2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
43857
- const project = await detectProject2(input.cwd || process.cwd());
43858
- const dataDir = await getProjectDataDir2(project.id);
43859
- const allObs = await loadObservationsJson2(dataDir);
43860
- if (allObs.length > 0) {
43861
- const PRIORITY_ORDER = {
43862
- "gotcha": 6,
43863
- "decision": 5,
43864
- "problem-solution": 4,
43865
- "trade-off": 3,
43866
- "discovery": 2,
43867
- "how-it-works": 1
43868
- };
43869
- const LOW_QUALITY_PATTERNS2 = [
43870
- /^Session activity/i,
43871
- /^Updated \S+\.\w+$/i,
43872
- // "Updated foo.ts" too generic
43873
- /^Created \S+\.\w+$/i,
43874
- // "Created bar.js"
43875
- /^Deleted \S+\.\w+$/i,
43876
- /^Modified \S+\.\w+$/i
43877
- ];
43878
- const isLowQuality2 = (title) => LOW_QUALITY_PATTERNS2.some((p2) => p2.test(title));
43879
- const scored = allObs.map((obs, i2) => {
43880
- const title = obs.title ?? "";
43881
- const hasFacts = (obs.facts?.length ?? 0) > 0;
43882
- const hasSubstance = title.length > 20 || hasFacts;
43883
- const quality = isLowQuality2(title) ? 0.1 : hasSubstance ? 1 : 0.5;
43884
- return {
43885
- obs,
43886
- priority: PRIORITY_ORDER[obs.type ?? ""] ?? 0,
43887
- quality,
43888
- recency: i2
43889
- // higher index = more recent
43890
- };
43891
- }).sort((a3, b3) => {
43892
- const scoreA = a3.priority * a3.quality;
43893
- const scoreB = b3.priority * b3.quality;
43894
- if (scoreB !== scoreA) return scoreB - scoreA;
43895
- return b3.recency - a3.recency;
43896
- });
43897
- const top = scored.slice(0, 5);
43898
- const TYPE_EMOJI = {
43899
- "gotcha": "\u{1F534}",
43900
- "decision": "\u{1F7E4}",
43901
- "problem-solution": "\u{1F7E1}",
43902
- "trade-off": "\u2696\uFE0F",
43903
- "discovery": "\u{1F7E3}",
43904
- "how-it-works": "\u{1F535}",
43905
- "what-changed": "\u{1F7E2}",
43906
- "why-it-exists": "\u{1F7E0}",
43907
- "session-request": "\u{1F3AF}"
43908
- };
43909
- const lines = top.map(({ obs }) => {
43910
- const emoji2 = TYPE_EMOJI[obs.type ?? ""] ?? "\u{1F4CC}";
43911
- const title = obs.title ?? "(untitled)";
43912
- const fact = obs.facts?.[0] ? ` \u2014 ${obs.facts[0]}` : "";
43913
- return `${emoji2} ${title}${fact}`;
43914
- });
43915
- contextSummary = `
43873
+ async function handleSessionStart(input) {
43874
+ let contextSummary = "";
43875
+ try {
43876
+ const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
43877
+ const { getProjectDataDir: getProjectDataDir2, loadObservationsJson: loadObservationsJson2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
43878
+ const project = await detectProject2(input.cwd || process.cwd());
43879
+ const dataDir = await getProjectDataDir2(project.id);
43880
+ const allObs = await loadObservationsJson2(dataDir);
43881
+ if (allObs.length > 0) {
43882
+ const PRIORITY_ORDER = {
43883
+ "gotcha": 6,
43884
+ "decision": 5,
43885
+ "problem-solution": 4,
43886
+ "trade-off": 3,
43887
+ "discovery": 2,
43888
+ "how-it-works": 1
43889
+ };
43890
+ const LOW_QUALITY_PATTERNS2 = [
43891
+ /^Session activity/i,
43892
+ /^Updated \S+\.\w+$/i,
43893
+ /^Created \S+\.\w+$/i,
43894
+ /^Deleted \S+\.\w+$/i,
43895
+ /^Modified \S+\.\w+$/i
43896
+ ];
43897
+ const isLowQuality2 = (title) => LOW_QUALITY_PATTERNS2.some((p2) => p2.test(title));
43898
+ const scored = allObs.map((obs, i2) => {
43899
+ const title = obs.title ?? "";
43900
+ const hasFacts = (obs.facts?.length ?? 0) > 0;
43901
+ const hasSubstance = title.length > 20 || hasFacts;
43902
+ const quality = isLowQuality2(title) ? 0.1 : hasSubstance ? 1 : 0.5;
43903
+ return { obs, priority: PRIORITY_ORDER[obs.type ?? ""] ?? 0, quality, recency: i2 };
43904
+ }).sort((a3, b3) => {
43905
+ const scoreA = a3.priority * a3.quality;
43906
+ const scoreB = b3.priority * b3.quality;
43907
+ if (scoreB !== scoreA) return scoreB - scoreA;
43908
+ return b3.recency - a3.recency;
43909
+ });
43910
+ const top = scored.slice(0, 5);
43911
+ const lines = top.map(({ obs }) => {
43912
+ const emoji2 = TYPE_EMOJI[obs.type ?? ""] ?? "\u{1F4CC}";
43913
+ const title = obs.title ?? "(untitled)";
43914
+ const fact = obs.facts?.[0] ? ` \u2014 ${obs.facts[0]}` : "";
43915
+ return `${emoji2} ${title}${fact}`;
43916
+ });
43917
+ contextSummary = `
43916
43918
 
43917
43919
  Recent project memories (${project.name}):
43918
43920
  ${lines.join("\n")}`;
43919
- }
43920
- } catch {
43921
- }
43922
- return {
43923
- observation: null,
43924
- output: {
43925
- continue: true,
43926
- systemMessage: `Memorix is active. Your memories from previous sessions are available via memorix_search.${contextSummary}`
43927
- }
43928
- };
43929
- }
43930
- case "pre_compact": {
43931
- const compactContent = extractContent(input);
43932
- if (compactContent.length < MIN_STORE_LENGTH) {
43933
- return { observation: null, output: defaultOutput };
43934
- }
43935
- return {
43936
- observation: buildObservation(input, compactContent),
43937
- output: defaultOutput
43938
- };
43939
- }
43940
- case "session_end":
43941
- return {
43942
- observation: buildObservation(input, extractContent(input)),
43943
- output: defaultOutput
43944
- };
43945
- case "post_edit": {
43946
- const editKey = `post_edit:${input.filePath ?? "general"}`;
43947
- if (isInCooldown(editKey)) {
43948
- return { observation: null, output: defaultOutput };
43949
- }
43950
- const editContent = extractContent(input);
43951
- if (editContent.length < MIN_EDIT_LENGTH) {
43952
- return { observation: null, output: defaultOutput };
43953
- }
43954
- const editPattern = detectBestPattern(editContent, 0.6);
43955
- if (!editPattern) {
43956
- return { observation: null, output: defaultOutput };
43957
- }
43958
- markTriggered(editKey);
43959
- return {
43960
- observation: buildObservation(input, editContent),
43961
- output: defaultOutput
43962
- };
43963
- }
43964
- case "post_command": {
43965
- if (input.command && NOISE_COMMANDS.some((r4) => r4.test(input.command))) {
43966
- return { observation: null, output: defaultOutput };
43967
- }
43968
- const cmdKey = `post_command:${input.command ?? "general"}`;
43969
- if (isInCooldown(cmdKey)) {
43970
- return { observation: null, output: defaultOutput };
43971
- }
43972
- const cmdContent = input.commandOutput || extractContent(input);
43973
- if (cmdContent.length < MIN_STORE_LENGTH) {
43974
- return { observation: null, output: defaultOutput };
43975
- }
43976
- detectBestPattern(cmdContent);
43977
- markTriggered(cmdKey);
43978
- return {
43979
- observation: buildObservation(input, cmdContent),
43980
- output: defaultOutput
43981
- };
43982
- }
43983
- case "post_tool": {
43984
- const toolKey = `post_tool:${input.toolName ?? "general"}`;
43985
- if (isInCooldown(toolKey)) {
43986
- return { observation: null, output: defaultOutput };
43987
- }
43988
- const toolContent = extractContent(input);
43989
- if (input.command) {
43990
- if (NOISE_COMMANDS.some((r4) => r4.test(input.command))) {
43991
- return { observation: null, output: defaultOutput };
43992
- }
43993
- if (toolContent.length < 50) {
43994
- return { observation: null, output: defaultOutput };
43995
- }
43996
- markTriggered(toolKey);
43997
- return {
43998
- observation: buildObservation(input, toolContent),
43999
- output: defaultOutput
44000
- };
44001
- }
44002
- if (toolContent.length < MIN_STORE_LENGTH) {
44003
- return { observation: null, output: defaultOutput };
44004
- }
44005
- const isFileModifyingTool = /^(write|edit|multi_?edit|multiedittool|create|patch|insert)/i.test(
44006
- input.toolName ?? ""
44007
- );
44008
- if (isFileModifyingTool) {
44009
- markTriggered(toolKey);
44010
- return {
44011
- observation: buildObservation(input, toolContent),
44012
- output: defaultOutput
44013
- };
44014
- }
44015
- const toolPattern = detectBestPattern(toolContent);
44016
- if (!toolPattern && toolContent.length < 200) {
44017
- return { observation: null, output: defaultOutput };
44018
- }
44019
- markTriggered(toolKey);
44020
- return {
44021
- observation: buildObservation(input, toolContent),
44022
- output: defaultOutput
44023
- };
44024
43921
  }
44025
- case "post_response":
44026
- case "user_prompt": {
44027
- const promptKey = `${input.event}:${input.sessionId ?? "general"}`;
44028
- if (isInCooldown(promptKey)) {
44029
- return { observation: null, output: defaultOutput };
44030
- }
44031
- const content = extractContent(input);
44032
- const minLen = input.event === "user_prompt" ? MIN_PROMPT_LENGTH : MIN_STORE_LENGTH;
44033
- if (content.length < minLen) {
44034
- return { observation: null, output: defaultOutput };
44035
- }
44036
- detectBestPattern(content);
44037
- markTriggered(promptKey);
44038
- return {
44039
- observation: buildObservation(input, content),
44040
- output: defaultOutput
44041
- };
43922
+ } catch {
43923
+ }
43924
+ return {
43925
+ observation: null,
43926
+ output: {
43927
+ continue: true,
43928
+ systemMessage: `Memorix is active. Your memories from previous sessions are available via memorix_search.${contextSummary}`
44042
43929
  }
44043
- default:
43930
+ };
43931
+ }
43932
+ async function handleHookEvent(input) {
43933
+ const defaultOutput = { continue: true };
43934
+ if (input.event === "session_start") {
43935
+ return handleSessionStart(input);
43936
+ }
43937
+ if (input.event === "session_end") {
43938
+ return {
43939
+ observation: buildObservation(input, extractContent(input), "unknown"),
43940
+ output: defaultOutput
43941
+ };
43942
+ }
43943
+ const category = classifyTool(input);
43944
+ const policy = STORAGE_POLICY[category] ?? STORAGE_POLICY.unknown;
43945
+ const content = extractContent(input);
43946
+ if (policy.store === "never") {
43947
+ return { observation: null, output: defaultOutput };
43948
+ }
43949
+ if (category === "command" && input.command && isNoiseCommand(input.command)) {
43950
+ return { observation: null, output: defaultOutput };
43951
+ }
43952
+ const minLen = input.event === "user_prompt" ? MIN_PROMPT_LENGTH : policy.minLength;
43953
+ if (content.length < minLen) {
43954
+ return { observation: null, output: defaultOutput };
43955
+ }
43956
+ const effectiveStore = input.event === "user_prompt" || input.event === "post_response" ? "always" : policy.store;
43957
+ if (effectiveStore === "if_substantial") {
43958
+ const pattern = detectBestPattern(content);
43959
+ if (!pattern && content.length < 200) {
44044
43960
  return { observation: null, output: defaultOutput };
43961
+ }
44045
43962
  }
43963
+ const cooldownKey = `${input.event}:${input.filePath ?? input.command ?? input.toolName ?? "general"}`;
43964
+ if (isInCooldown(cooldownKey)) {
43965
+ return { observation: null, output: defaultOutput };
43966
+ }
43967
+ markTriggered(cooldownKey);
43968
+ return {
43969
+ observation: buildObservation(input, content, category),
43970
+ output: defaultOutput
43971
+ };
44046
43972
  }
44047
43973
  async function runHook() {
44048
43974
  const chunks = [];
@@ -44072,17 +43998,6 @@ async function runHook() {
44072
43998
  const dataDir = await getProjectDataDir2(project.id);
44073
43999
  await initObservations2(dataDir);
44074
44000
  await storeObservation2({ ...observation, projectId: project.id });
44075
- const TYPE_EMOJI = {
44076
- "gotcha": "\u{1F534}",
44077
- "decision": "\u{1F7E4}",
44078
- "problem-solution": "\u{1F7E1}",
44079
- "trade-off": "\u2696\uFE0F",
44080
- "discovery": "\u{1F7E3}",
44081
- "how-it-works": "\u{1F535}",
44082
- "what-changed": "\u{1F7E2}",
44083
- "why-it-exists": "\u{1F7E0}",
44084
- "session-request": "\u{1F3AF}"
44085
- };
44086
44001
  const emoji2 = TYPE_EMOJI[observation.type] ?? "\u{1F4DD}";
44087
44002
  output.systemMessage = (output.systemMessage ?? "") + `
44088
44003
  ${emoji2} Memorix saved: ${observation.title} [${observation.type}]`;
@@ -44091,26 +44006,41 @@ ${emoji2} Memorix saved: ${observation.title} [${observation.type}]`;
44091
44006
  }
44092
44007
  process.stdout.write(JSON.stringify(output));
44093
44008
  }
44094
- var cooldowns, COOLDOWN_MS, MIN_STORE_LENGTH, MIN_PROMPT_LENGTH, MIN_EDIT_LENGTH, NOISE_COMMANDS, MAX_CONTENT_LENGTH;
44009
+ var TYPE_EMOJI, cooldowns, COOLDOWN_MS, MIN_PROMPT_LENGTH, MAX_CONTENT_LENGTH, NOISE_COMMANDS, STORAGE_POLICY;
44095
44010
  var init_handler = __esm({
44096
44011
  "src/hooks/handler.ts"() {
44097
44012
  "use strict";
44098
44013
  init_esm_shims();
44099
44014
  init_normalizer();
44100
44015
  init_pattern_detector();
44016
+ TYPE_EMOJI = {
44017
+ "gotcha": "\u{1F534}",
44018
+ "decision": "\u{1F7E4}",
44019
+ "problem-solution": "\u{1F7E1}",
44020
+ "trade-off": "\u2696\uFE0F",
44021
+ "discovery": "\u{1F7E3}",
44022
+ "how-it-works": "\u{1F535}",
44023
+ "what-changed": "\u{1F7E2}",
44024
+ "why-it-exists": "\u{1F7E0}",
44025
+ "session-request": "\u{1F3AF}"
44026
+ };
44101
44027
  cooldowns = /* @__PURE__ */ new Map();
44102
44028
  COOLDOWN_MS = 3e4;
44103
- MIN_STORE_LENGTH = 100;
44104
44029
  MIN_PROMPT_LENGTH = 20;
44105
- MIN_EDIT_LENGTH = 30;
44030
+ MAX_CONTENT_LENGTH = 4e3;
44106
44031
  NOISE_COMMANDS = [
44107
- /^(ls|dir|cd|pwd|echo|cat|type|head|tail|wc|find|which|where|whoami)\b/i,
44108
- /^(Get-Content|Test-Path|Get-Item|Get-ChildItem|Set-Location|Write-Host)\b/i,
44109
- /^(Start-Sleep|Select-String|Select-Object|Format-Table|Measure-Object)\b/i,
44110
- /^(mkdir|rm|cp|mv|touch|chmod|chown)\b/i,
44111
- /^(node -[ep]|python -c)\b/i
44032
+ /^(ls|dir|cd|pwd|echo|cat|type|head|tail|wc|which|where|whoami)(\s|$)/i,
44033
+ /^(Get-Content|Test-Path|Get-Item|Get-ChildItem|Set-Location|Write-Host)(\s|$)/i,
44034
+ /^(Start-Sleep|Select-String|Select-Object|Format-Table|Measure-Object)(\s|$)/i
44112
44035
  ];
44113
- MAX_CONTENT_LENGTH = 4e3;
44036
+ STORAGE_POLICY = {
44037
+ file_modify: { store: "always", minLength: 50, defaultType: "what-changed" },
44038
+ command: { store: "always", minLength: 30, defaultType: "discovery" },
44039
+ file_read: { store: "if_substantial", minLength: 200, defaultType: "discovery" },
44040
+ search: { store: "if_substantial", minLength: 200, defaultType: "discovery" },
44041
+ memorix_internal: { store: "never", minLength: 0, defaultType: "discovery" },
44042
+ unknown: { store: "if_substantial", minLength: 100, defaultType: "discovery" }
44043
+ };
44114
44044
  }
44115
44045
  });
44116
44046