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 +13 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli/index.js +153 -223
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
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-
|
|
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-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
43847
|
-
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
43854
|
-
|
|
43855
|
-
|
|
43856
|
-
|
|
43857
|
-
|
|
43858
|
-
|
|
43859
|
-
|
|
43860
|
-
|
|
43861
|
-
|
|
43862
|
-
|
|
43863
|
-
|
|
43864
|
-
|
|
43865
|
-
|
|
43866
|
-
|
|
43867
|
-
|
|
43868
|
-
|
|
43869
|
-
|
|
43870
|
-
|
|
43871
|
-
|
|
43872
|
-
|
|
43873
|
-
|
|
43874
|
-
|
|
43875
|
-
|
|
43876
|
-
|
|
43877
|
-
|
|
43878
|
-
|
|
43879
|
-
|
|
43880
|
-
|
|
43881
|
-
|
|
43882
|
-
|
|
43883
|
-
|
|
43884
|
-
|
|
43885
|
-
|
|
43886
|
-
|
|
43887
|
-
|
|
43888
|
-
|
|
43889
|
-
|
|
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
|
-
|
|
44026
|
-
|
|
44027
|
-
|
|
44028
|
-
|
|
44029
|
-
|
|
44030
|
-
|
|
44031
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
44030
|
+
MAX_CONTENT_LENGTH = 4e3;
|
|
44106
44031
|
NOISE_COMMANDS = [
|
|
44107
|
-
/^(ls|dir|cd|pwd|echo|cat|type|head|tail|wc|
|
|
44108
|
-
/^(Get-Content|Test-Path|Get-Item|Get-ChildItem|Set-Location|Write-Host)\
|
|
44109
|
-
/^(Start-Sleep|Select-String|Select-Object|Format-Table|Measure-Object)\
|
|
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
|
-
|
|
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
|
|