memorix 0.9.14 → 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 +21 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli/index.js +159 -199
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
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
|
+
|
|
18
|
+
## [0.9.15] — 2026-02-26
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Feedback visibility** — Hook auto-stores were silent. Now returns `systemMessage` to the agent after each save, e.g. `🟢 Memorix saved: Updated auth.ts [what-changed]`. Gives Codex-like visibility into what memorix is recording.
|
|
22
|
+
- **File-modifying tools always store** — Write/Edit/MultiEdit tool events were rejected when content lacked pattern keywords (e.g., writing utility functions with no "error"/"fix" keywords). Now file-modifying tools always store if content > 100 chars, classified as `what-changed` by default.
|
|
23
|
+
- **PreCompact low-quality spam** — PreCompact events stored empty/minimal observations with no meaningful content. Now requires `MIN_STORE_LENGTH` (100 chars) to store.
|
|
24
|
+
- **Normalizer prompt extraction** — `normalizeClaude` only extracted `prompt` for `user_prompt` events. Now extracts for all events (PreCompact, etc.), preserving context that would otherwise be lost.
|
|
25
|
+
|
|
5
26
|
## [0.9.14] — 2026-02-26
|
|
6
27
|
|
|
7
28
|
### 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
|
}
|
|
@@ -43369,8 +43369,8 @@ function normalizeClaude(payload, event) {
|
|
|
43369
43369
|
result.filePath = toolInput?.file_path ?? toolInput?.filePath;
|
|
43370
43370
|
}
|
|
43371
43371
|
}
|
|
43372
|
-
if (
|
|
43373
|
-
result.userPrompt = payload.prompt
|
|
43372
|
+
if (payload.prompt) {
|
|
43373
|
+
result.userPrompt = payload.prompt;
|
|
43374
43374
|
}
|
|
43375
43375
|
return result;
|
|
43376
43376
|
}
|
|
@@ -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,18 @@ 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;
|
|
43857
|
+
const obsType = pattern ? patternToObservationType(pattern.type) : fallbackType;
|
|
43830
43858
|
return {
|
|
43831
43859
|
entityName: deriveEntityName(input),
|
|
43832
43860
|
type: obsType,
|
|
@@ -43836,197 +43864,111 @@ function buildObservation(input, content) {
|
|
|
43836
43864
|
`Agent: ${input.agent}`,
|
|
43837
43865
|
`Session: ${input.sessionId}`,
|
|
43838
43866
|
...input.filePath ? [`File: ${input.filePath}`] : [],
|
|
43839
|
-
...input.command ? [`Command: ${input.command}`] : []
|
|
43867
|
+
...input.command ? [`Command: ${extractRealCommand(input.command)}`] : []
|
|
43840
43868
|
],
|
|
43841
43869
|
concepts: pattern?.matchedKeywords ?? [],
|
|
43842
43870
|
filesModified: input.filePath ? [input.filePath] : []
|
|
43843
43871
|
};
|
|
43844
43872
|
}
|
|
43845
|
-
async function
|
|
43846
|
-
|
|
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
|
-
}).sort((a3, b3) => {
|
|
43891
|
-
const scoreA = a3.priority * a3.quality;
|
|
43892
|
-
const scoreB = b3.priority * b3.quality;
|
|
43893
|
-
if (scoreB !== scoreA) return scoreB - scoreA;
|
|
43894
|
-
return b3.recency - a3.recency;
|
|
43895
|
-
});
|
|
43896
|
-
const top = scored.slice(0, 5);
|
|
43897
|
-
const TYPE_EMOJI = {
|
|
43898
|
-
"gotcha": "\u{1F534}",
|
|
43899
|
-
"decision": "\u{1F7E4}",
|
|
43900
|
-
"problem-solution": "\u{1F7E1}",
|
|
43901
|
-
"trade-off": "\u2696\uFE0F",
|
|
43902
|
-
"discovery": "\u{1F7E3}",
|
|
43903
|
-
"how-it-works": "\u{1F535}",
|
|
43904
|
-
"what-changed": "\u{1F7E2}",
|
|
43905
|
-
"why-it-exists": "\u{1F7E0}",
|
|
43906
|
-
"session-request": "\u{1F3AF}"
|
|
43907
|
-
};
|
|
43908
|
-
const lines = top.map(({ obs }) => {
|
|
43909
|
-
const emoji2 = TYPE_EMOJI[obs.type ?? ""] ?? "\u{1F4CC}";
|
|
43910
|
-
const title = obs.title ?? "(untitled)";
|
|
43911
|
-
const fact = obs.facts?.[0] ? ` \u2014 ${obs.facts[0]}` : "";
|
|
43912
|
-
return `${emoji2} ${title}${fact}`;
|
|
43913
|
-
});
|
|
43914
|
-
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 = `
|
|
43915
43918
|
|
|
43916
43919
|
Recent project memories (${project.name}):
|
|
43917
43920
|
${lines.join("\n")}`;
|
|
43918
|
-
}
|
|
43919
|
-
} catch {
|
|
43920
|
-
}
|
|
43921
|
-
return {
|
|
43922
|
-
observation: null,
|
|
43923
|
-
output: {
|
|
43924
|
-
continue: true,
|
|
43925
|
-
systemMessage: `Memorix is active. Your memories from previous sessions are available via memorix_search.${contextSummary}`
|
|
43926
|
-
}
|
|
43927
|
-
};
|
|
43928
43921
|
}
|
|
43929
|
-
|
|
43930
|
-
|
|
43931
|
-
|
|
43932
|
-
|
|
43933
|
-
|
|
43934
|
-
|
|
43935
|
-
|
|
43936
|
-
observation: buildObservation(input, extractContent(input)),
|
|
43937
|
-
output: defaultOutput
|
|
43938
|
-
};
|
|
43939
|
-
case "post_edit": {
|
|
43940
|
-
const editKey = `post_edit:${input.filePath ?? "general"}`;
|
|
43941
|
-
if (isInCooldown(editKey)) {
|
|
43942
|
-
return { observation: null, output: defaultOutput };
|
|
43943
|
-
}
|
|
43944
|
-
const editContent = extractContent(input);
|
|
43945
|
-
if (editContent.length < MIN_EDIT_LENGTH) {
|
|
43946
|
-
return { observation: null, output: defaultOutput };
|
|
43947
|
-
}
|
|
43948
|
-
const editPattern = detectBestPattern(editContent, 0.6);
|
|
43949
|
-
if (!editPattern) {
|
|
43950
|
-
return { observation: null, output: defaultOutput };
|
|
43951
|
-
}
|
|
43952
|
-
markTriggered(editKey);
|
|
43953
|
-
return {
|
|
43954
|
-
observation: buildObservation(input, editContent),
|
|
43955
|
-
output: defaultOutput
|
|
43956
|
-
};
|
|
43957
|
-
}
|
|
43958
|
-
case "post_command": {
|
|
43959
|
-
if (input.command && NOISE_COMMANDS.some((r4) => r4.test(input.command))) {
|
|
43960
|
-
return { observation: null, output: defaultOutput };
|
|
43961
|
-
}
|
|
43962
|
-
const cmdKey = `post_command:${input.command ?? "general"}`;
|
|
43963
|
-
if (isInCooldown(cmdKey)) {
|
|
43964
|
-
return { observation: null, output: defaultOutput };
|
|
43965
|
-
}
|
|
43966
|
-
const cmdContent = input.commandOutput || extractContent(input);
|
|
43967
|
-
if (cmdContent.length < MIN_STORE_LENGTH) {
|
|
43968
|
-
return { observation: null, output: defaultOutput };
|
|
43969
|
-
}
|
|
43970
|
-
detectBestPattern(cmdContent);
|
|
43971
|
-
markTriggered(cmdKey);
|
|
43972
|
-
return {
|
|
43973
|
-
observation: buildObservation(input, cmdContent),
|
|
43974
|
-
output: defaultOutput
|
|
43975
|
-
};
|
|
43976
|
-
}
|
|
43977
|
-
case "post_tool": {
|
|
43978
|
-
const toolKey = `post_tool:${input.toolName ?? "general"}`;
|
|
43979
|
-
if (isInCooldown(toolKey)) {
|
|
43980
|
-
return { observation: null, output: defaultOutput };
|
|
43981
|
-
}
|
|
43982
|
-
const toolContent = extractContent(input);
|
|
43983
|
-
if (input.command) {
|
|
43984
|
-
if (NOISE_COMMANDS.some((r4) => r4.test(input.command))) {
|
|
43985
|
-
return { observation: null, output: defaultOutput };
|
|
43986
|
-
}
|
|
43987
|
-
if (toolContent.length < 50) {
|
|
43988
|
-
return { observation: null, output: defaultOutput };
|
|
43989
|
-
}
|
|
43990
|
-
markTriggered(toolKey);
|
|
43991
|
-
return {
|
|
43992
|
-
observation: buildObservation(input, toolContent),
|
|
43993
|
-
output: defaultOutput
|
|
43994
|
-
};
|
|
43995
|
-
}
|
|
43996
|
-
if (toolContent.length < MIN_STORE_LENGTH) {
|
|
43997
|
-
return { observation: null, output: defaultOutput };
|
|
43998
|
-
}
|
|
43999
|
-
const toolPattern = detectBestPattern(toolContent);
|
|
44000
|
-
if (!toolPattern && toolContent.length < 200) {
|
|
44001
|
-
return { observation: null, output: defaultOutput };
|
|
44002
|
-
}
|
|
44003
|
-
markTriggered(toolKey);
|
|
44004
|
-
return {
|
|
44005
|
-
observation: buildObservation(input, toolContent),
|
|
44006
|
-
output: defaultOutput
|
|
44007
|
-
};
|
|
44008
|
-
}
|
|
44009
|
-
case "post_response":
|
|
44010
|
-
case "user_prompt": {
|
|
44011
|
-
const promptKey = `${input.event}:${input.sessionId ?? "general"}`;
|
|
44012
|
-
if (isInCooldown(promptKey)) {
|
|
44013
|
-
return { observation: null, output: defaultOutput };
|
|
44014
|
-
}
|
|
44015
|
-
const content = extractContent(input);
|
|
44016
|
-
const minLen = input.event === "user_prompt" ? MIN_PROMPT_LENGTH : MIN_STORE_LENGTH;
|
|
44017
|
-
if (content.length < minLen) {
|
|
44018
|
-
return { observation: null, output: defaultOutput };
|
|
44019
|
-
}
|
|
44020
|
-
detectBestPattern(content);
|
|
44021
|
-
markTriggered(promptKey);
|
|
44022
|
-
return {
|
|
44023
|
-
observation: buildObservation(input, content),
|
|
44024
|
-
output: defaultOutput
|
|
44025
|
-
};
|
|
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}`
|
|
44026
43929
|
}
|
|
44027
|
-
|
|
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) {
|
|
44028
43960
|
return { observation: null, output: defaultOutput };
|
|
43961
|
+
}
|
|
44029
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
|
+
};
|
|
44030
43972
|
}
|
|
44031
43973
|
async function runHook() {
|
|
44032
43974
|
const chunks = [];
|
|
@@ -44056,31 +43998,49 @@ async function runHook() {
|
|
|
44056
43998
|
const dataDir = await getProjectDataDir2(project.id);
|
|
44057
43999
|
await initObservations2(dataDir);
|
|
44058
44000
|
await storeObservation2({ ...observation, projectId: project.id });
|
|
44001
|
+
const emoji2 = TYPE_EMOJI[observation.type] ?? "\u{1F4DD}";
|
|
44002
|
+
output.systemMessage = (output.systemMessage ?? "") + `
|
|
44003
|
+
${emoji2} Memorix saved: ${observation.title} [${observation.type}]`;
|
|
44059
44004
|
} catch {
|
|
44060
44005
|
}
|
|
44061
44006
|
}
|
|
44062
44007
|
process.stdout.write(JSON.stringify(output));
|
|
44063
44008
|
}
|
|
44064
|
-
var cooldowns, COOLDOWN_MS,
|
|
44009
|
+
var TYPE_EMOJI, cooldowns, COOLDOWN_MS, MIN_PROMPT_LENGTH, MAX_CONTENT_LENGTH, NOISE_COMMANDS, STORAGE_POLICY;
|
|
44065
44010
|
var init_handler = __esm({
|
|
44066
44011
|
"src/hooks/handler.ts"() {
|
|
44067
44012
|
"use strict";
|
|
44068
44013
|
init_esm_shims();
|
|
44069
44014
|
init_normalizer();
|
|
44070
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
|
+
};
|
|
44071
44027
|
cooldowns = /* @__PURE__ */ new Map();
|
|
44072
44028
|
COOLDOWN_MS = 3e4;
|
|
44073
|
-
MIN_STORE_LENGTH = 100;
|
|
44074
44029
|
MIN_PROMPT_LENGTH = 20;
|
|
44075
|
-
|
|
44030
|
+
MAX_CONTENT_LENGTH = 4e3;
|
|
44076
44031
|
NOISE_COMMANDS = [
|
|
44077
|
-
/^(ls|dir|cd|pwd|echo|cat|type|head|tail|wc|
|
|
44078
|
-
/^(Get-Content|Test-Path|Get-Item|Get-ChildItem|Set-Location|Write-Host)\
|
|
44079
|
-
/^(Start-Sleep|Select-String|Select-Object|Format-Table|Measure-Object)\
|
|
44080
|
-
/^(mkdir|rm|cp|mv|touch|chmod|chown)\b/i,
|
|
44081
|
-
/^(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
|
|
44082
44035
|
];
|
|
44083
|
-
|
|
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
|
+
};
|
|
44084
44044
|
}
|
|
44085
44045
|
});
|
|
44086
44046
|
|