memorix 0.6.3 → 0.6.5
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/dist/cli/index.js +232 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +236 -10
- package/dist/dashboard/static/style.css +101 -11
- package/dist/index.js +73 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -52,6 +52,9 @@ function sanitizeProjectId(projectId) {
|
|
|
52
52
|
return projectId.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
|
|
53
53
|
}
|
|
54
54
|
async function getProjectDataDir(projectId, baseDir) {
|
|
55
|
+
if (projectId === "__invalid__") {
|
|
56
|
+
throw new Error("Cannot create data directory for invalid project");
|
|
57
|
+
}
|
|
55
58
|
const base = baseDir ?? DEFAULT_DATA_DIR;
|
|
56
59
|
const dirName = sanitizeProjectId(projectId);
|
|
57
60
|
const dataDir = path2.join(base, dirName);
|
|
@@ -1223,6 +1226,7 @@ __export(detector_exports, {
|
|
|
1223
1226
|
});
|
|
1224
1227
|
import { execSync } from "child_process";
|
|
1225
1228
|
import { existsSync } from "fs";
|
|
1229
|
+
import os2 from "os";
|
|
1226
1230
|
import path3 from "path";
|
|
1227
1231
|
function detectProject(cwd) {
|
|
1228
1232
|
const basePath = cwd ?? process.cwd();
|
|
@@ -1233,11 +1237,77 @@ function detectProject(cwd) {
|
|
|
1233
1237
|
const name2 = id2.split("/").pop() ?? path3.basename(rootPath);
|
|
1234
1238
|
return { id: id2, name: name2, gitRemote, rootPath };
|
|
1235
1239
|
}
|
|
1240
|
+
if (!isValidProjectRoot(rootPath)) {
|
|
1241
|
+
console.error(`[memorix] Skipped invalid project root: ${rootPath}`);
|
|
1242
|
+
return { id: "__invalid__", name: "unknown", rootPath };
|
|
1243
|
+
}
|
|
1236
1244
|
const name = path3.basename(rootPath);
|
|
1237
1245
|
const id = `local/${name}`;
|
|
1238
1246
|
console.error(`[memorix] Warning: no git remote found at ${rootPath}, using fallback projectId: ${id}`);
|
|
1239
1247
|
return { id, name, rootPath };
|
|
1240
1248
|
}
|
|
1249
|
+
function isValidProjectRoot(dirPath) {
|
|
1250
|
+
const resolved = path3.resolve(dirPath);
|
|
1251
|
+
const home = path3.resolve(os2.homedir());
|
|
1252
|
+
if (resolved === home) return false;
|
|
1253
|
+
if (resolved === path3.parse(resolved).root) return false;
|
|
1254
|
+
const basename2 = path3.basename(resolved).toLowerCase();
|
|
1255
|
+
const knownNonProjectDirs = /* @__PURE__ */ new Set([
|
|
1256
|
+
// IDE / editor config dirs
|
|
1257
|
+
".vscode",
|
|
1258
|
+
".cursor",
|
|
1259
|
+
".windsurf",
|
|
1260
|
+
".kiro",
|
|
1261
|
+
".codex",
|
|
1262
|
+
".gemini",
|
|
1263
|
+
".claude",
|
|
1264
|
+
".github",
|
|
1265
|
+
".git",
|
|
1266
|
+
// OS / system dirs
|
|
1267
|
+
"desktop",
|
|
1268
|
+
"documents",
|
|
1269
|
+
"downloads",
|
|
1270
|
+
"pictures",
|
|
1271
|
+
"videos",
|
|
1272
|
+
"music",
|
|
1273
|
+
"appdata",
|
|
1274
|
+
"application data",
|
|
1275
|
+
"library",
|
|
1276
|
+
// Package manager / tool dirs
|
|
1277
|
+
"node_modules",
|
|
1278
|
+
".npm",
|
|
1279
|
+
".yarn",
|
|
1280
|
+
".pnpm-store",
|
|
1281
|
+
".config",
|
|
1282
|
+
".local",
|
|
1283
|
+
".cache",
|
|
1284
|
+
".ssh",
|
|
1285
|
+
".memorix"
|
|
1286
|
+
]);
|
|
1287
|
+
if (knownNonProjectDirs.has(basename2)) {
|
|
1288
|
+
const parent = path3.resolve(path3.dirname(resolved));
|
|
1289
|
+
if (parent === home || parent === path3.parse(parent).root) {
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
const projectIndicators = [
|
|
1294
|
+
"package.json",
|
|
1295
|
+
"Cargo.toml",
|
|
1296
|
+
"go.mod",
|
|
1297
|
+
"pyproject.toml",
|
|
1298
|
+
"setup.py",
|
|
1299
|
+
"pom.xml",
|
|
1300
|
+
"build.gradle",
|
|
1301
|
+
"Makefile",
|
|
1302
|
+
"CMakeLists.txt",
|
|
1303
|
+
"composer.json",
|
|
1304
|
+
"Gemfile",
|
|
1305
|
+
".git",
|
|
1306
|
+
"README.md",
|
|
1307
|
+
"README"
|
|
1308
|
+
];
|
|
1309
|
+
return projectIndicators.some((f) => existsSync(path3.join(resolved, f)));
|
|
1310
|
+
}
|
|
1241
1311
|
function findPackageRoot(cwd) {
|
|
1242
1312
|
let dir = path3.resolve(cwd);
|
|
1243
1313
|
const root = path3.parse(dir).root;
|
|
@@ -3164,7 +3234,7 @@ __export(installers_exports, {
|
|
|
3164
3234
|
});
|
|
3165
3235
|
import * as fs3 from "fs/promises";
|
|
3166
3236
|
import * as path5 from "path";
|
|
3167
|
-
import * as
|
|
3237
|
+
import * as os3 from "os";
|
|
3168
3238
|
import { createRequire } from "module";
|
|
3169
3239
|
function resolveHookCommand() {
|
|
3170
3240
|
if (process.platform === "win32") {
|
|
@@ -3269,7 +3339,7 @@ function getProjectConfigPath(agent, projectRoot) {
|
|
|
3269
3339
|
}
|
|
3270
3340
|
}
|
|
3271
3341
|
function getGlobalConfigPath(agent) {
|
|
3272
|
-
const home =
|
|
3342
|
+
const home = os3.homedir();
|
|
3273
3343
|
switch (agent) {
|
|
3274
3344
|
case "claude":
|
|
3275
3345
|
case "copilot":
|
|
@@ -3284,7 +3354,7 @@ function getGlobalConfigPath(agent) {
|
|
|
3284
3354
|
}
|
|
3285
3355
|
async function detectInstalledAgents() {
|
|
3286
3356
|
const agents = [];
|
|
3287
|
-
const home =
|
|
3357
|
+
const home = os3.homedir();
|
|
3288
3358
|
const claudeDir = path5.join(home, ".claude");
|
|
3289
3359
|
try {
|
|
3290
3360
|
await fs3.access(claudeDir);
|
|
@@ -5029,9 +5099,10 @@ function normalizeCursor(payload, event) {
|
|
|
5029
5099
|
return result;
|
|
5030
5100
|
}
|
|
5031
5101
|
function normalizeHookInput(payload) {
|
|
5102
|
+
const directEvent = typeof payload.event === "string" ? EVENT_MAP[payload.event] : void 0;
|
|
5032
5103
|
const agent = detectAgent(payload);
|
|
5033
5104
|
const rawEventName = extractEventName(payload, agent);
|
|
5034
|
-
const event = EVENT_MAP[rawEventName] ?? "post_tool";
|
|
5105
|
+
const event = directEvent ?? EVENT_MAP[rawEventName] ?? "post_tool";
|
|
5035
5106
|
const timestamp = payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5036
5107
|
let agentSpecific = {};
|
|
5037
5108
|
switch (agent) {
|
|
@@ -5064,6 +5135,16 @@ var init_normalizer = __esm({
|
|
|
5064
5135
|
"use strict";
|
|
5065
5136
|
init_esm_shims();
|
|
5066
5137
|
EVENT_MAP = {
|
|
5138
|
+
// Identity mappings — already-normalized event names
|
|
5139
|
+
// This allows direct payloads like { event: 'session_start' } to work
|
|
5140
|
+
session_start: "session_start",
|
|
5141
|
+
user_prompt: "user_prompt",
|
|
5142
|
+
post_edit: "post_edit",
|
|
5143
|
+
post_command: "post_command",
|
|
5144
|
+
post_tool: "post_tool",
|
|
5145
|
+
pre_compact: "pre_compact",
|
|
5146
|
+
session_end: "session_end",
|
|
5147
|
+
post_response: "post_response",
|
|
5067
5148
|
// Claude Code / VS Code Copilot
|
|
5068
5149
|
SessionStart: "session_start",
|
|
5069
5150
|
UserPromptSubmit: "user_prompt",
|
|
@@ -5324,13 +5405,32 @@ async function handleHookEvent(input) {
|
|
|
5324
5405
|
"discovery": 2,
|
|
5325
5406
|
"how-it-works": 1
|
|
5326
5407
|
};
|
|
5327
|
-
const
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5408
|
+
const LOW_QUALITY_PATTERNS2 = [
|
|
5409
|
+
/^Session activity/i,
|
|
5410
|
+
/^Updated \S+\.\w+$/i,
|
|
5411
|
+
// "Updated foo.ts" — too generic
|
|
5412
|
+
/^Created \S+\.\w+$/i,
|
|
5413
|
+
// "Created bar.js"
|
|
5414
|
+
/^Deleted \S+\.\w+$/i,
|
|
5415
|
+
/^Modified \S+\.\w+$/i
|
|
5416
|
+
];
|
|
5417
|
+
const isLowQuality2 = (title) => LOW_QUALITY_PATTERNS2.some((p3) => p3.test(title));
|
|
5418
|
+
const scored = allObs.map((obs, i) => {
|
|
5419
|
+
const title = obs.title ?? "";
|
|
5420
|
+
const hasFacts = (obs.facts?.length ?? 0) > 0;
|
|
5421
|
+
const hasSubstance = title.length > 20 || hasFacts;
|
|
5422
|
+
const quality = isLowQuality2(title) ? 0.1 : hasSubstance ? 1 : 0.5;
|
|
5423
|
+
return {
|
|
5424
|
+
obs,
|
|
5425
|
+
priority: PRIORITY_ORDER[obs.type ?? ""] ?? 0,
|
|
5426
|
+
quality,
|
|
5427
|
+
recency: i
|
|
5428
|
+
// higher index = more recent
|
|
5429
|
+
};
|
|
5430
|
+
}).sort((a, b) => {
|
|
5431
|
+
const scoreA = a.priority * a.quality;
|
|
5432
|
+
const scoreB = b.priority * b.quality;
|
|
5433
|
+
if (scoreB !== scoreA) return scoreB - scoreA;
|
|
5334
5434
|
return b.recency - a.recency;
|
|
5335
5435
|
});
|
|
5336
5436
|
const top = scored.slice(0, 5);
|
|
@@ -5750,13 +5850,130 @@ var init_dashboard = __esm({
|
|
|
5750
5850
|
}
|
|
5751
5851
|
});
|
|
5752
5852
|
|
|
5853
|
+
// src/cli/commands/cleanup.ts
|
|
5854
|
+
var cleanup_exports = {};
|
|
5855
|
+
__export(cleanup_exports, {
|
|
5856
|
+
default: () => cleanup_default
|
|
5857
|
+
});
|
|
5858
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
5859
|
+
function isLowQuality(title) {
|
|
5860
|
+
return LOW_QUALITY_PATTERNS.some((p3) => p3.test(title.trim()));
|
|
5861
|
+
}
|
|
5862
|
+
var LOW_QUALITY_PATTERNS, cleanup_default;
|
|
5863
|
+
var init_cleanup = __esm({
|
|
5864
|
+
"src/cli/commands/cleanup.ts"() {
|
|
5865
|
+
"use strict";
|
|
5866
|
+
init_esm_shims();
|
|
5867
|
+
init_detector();
|
|
5868
|
+
init_persistence();
|
|
5869
|
+
LOW_QUALITY_PATTERNS = [
|
|
5870
|
+
/^Session activity/i,
|
|
5871
|
+
/^Updated \S+\.\w+$/i,
|
|
5872
|
+
/^Created \S+\.\w+$/i,
|
|
5873
|
+
/^Deleted \S+\.\w+$/i,
|
|
5874
|
+
/^Modified \S+\.\w+$/i,
|
|
5875
|
+
/^Ran command:/i,
|
|
5876
|
+
/^Read file:/i
|
|
5877
|
+
];
|
|
5878
|
+
cleanup_default = defineCommand10({
|
|
5879
|
+
meta: {
|
|
5880
|
+
name: "cleanup",
|
|
5881
|
+
description: "Remove low-quality auto-generated observations"
|
|
5882
|
+
},
|
|
5883
|
+
args: {
|
|
5884
|
+
dry: {
|
|
5885
|
+
type: "boolean",
|
|
5886
|
+
description: "Preview only \u2014 do not delete anything",
|
|
5887
|
+
default: false
|
|
5888
|
+
},
|
|
5889
|
+
force: {
|
|
5890
|
+
type: "boolean",
|
|
5891
|
+
description: "Delete without confirmation",
|
|
5892
|
+
default: false
|
|
5893
|
+
}
|
|
5894
|
+
},
|
|
5895
|
+
async run({ args }) {
|
|
5896
|
+
const project = detectProject();
|
|
5897
|
+
if (project.id === "__invalid__") {
|
|
5898
|
+
console.error("\u274C Not in a valid project directory.");
|
|
5899
|
+
process.exit(1);
|
|
5900
|
+
}
|
|
5901
|
+
console.log(`
|
|
5902
|
+
\u{1F4E6} Project: ${project.name} (${project.id})
|
|
5903
|
+
`);
|
|
5904
|
+
const dataDir = await getProjectDataDir(project.id);
|
|
5905
|
+
const allObs = await loadObservationsJson(dataDir);
|
|
5906
|
+
if (allObs.length === 0) {
|
|
5907
|
+
console.log("\u2705 No observations found \u2014 nothing to clean up.");
|
|
5908
|
+
return;
|
|
5909
|
+
}
|
|
5910
|
+
const lowQuality = allObs.filter((o) => isLowQuality(o.title ?? ""));
|
|
5911
|
+
const highQuality = allObs.filter((o) => !isLowQuality(o.title ?? ""));
|
|
5912
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5913
|
+
const duplicates = [];
|
|
5914
|
+
const unique = [];
|
|
5915
|
+
for (const obs of highQuality) {
|
|
5916
|
+
const key = `${obs.type}|${obs.title}|${obs.entityName}`;
|
|
5917
|
+
if (seen.has(key)) {
|
|
5918
|
+
duplicates.push(obs);
|
|
5919
|
+
} else {
|
|
5920
|
+
seen.add(key);
|
|
5921
|
+
unique.push(obs);
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5924
|
+
const toRemove = [...lowQuality, ...duplicates];
|
|
5925
|
+
console.log(`\u{1F4CA} Analysis:`);
|
|
5926
|
+
console.log(` Total observations: ${allObs.length}`);
|
|
5927
|
+
console.log(` \u{1F7E2} High quality: ${unique.length}`);
|
|
5928
|
+
console.log(` \u{1F534} Low quality: ${lowQuality.length}`);
|
|
5929
|
+
console.log(` \u{1F7E1} Duplicates: ${duplicates.length}`);
|
|
5930
|
+
console.log(` \u{1F5D1}\uFE0F To remove: ${toRemove.length}`);
|
|
5931
|
+
console.log();
|
|
5932
|
+
if (toRemove.length === 0) {
|
|
5933
|
+
console.log("\u2705 All observations are high quality \u2014 nothing to clean up!");
|
|
5934
|
+
return;
|
|
5935
|
+
}
|
|
5936
|
+
console.log("\u{1F50D} Examples of items to remove:");
|
|
5937
|
+
toRemove.slice(0, 10).forEach((o) => {
|
|
5938
|
+
const tag = isLowQuality(o.title ?? "") ? "(low-quality)" : "(duplicate)";
|
|
5939
|
+
console.log(` ${tag} #${o.id ?? "?"} "${o.title}" [${o.type}]`);
|
|
5940
|
+
});
|
|
5941
|
+
if (toRemove.length > 10) {
|
|
5942
|
+
console.log(` ... and ${toRemove.length - 10} more`);
|
|
5943
|
+
}
|
|
5944
|
+
console.log();
|
|
5945
|
+
if (args.dry) {
|
|
5946
|
+
console.log("\u{1F512} Dry run \u2014 no changes made.");
|
|
5947
|
+
return;
|
|
5948
|
+
}
|
|
5949
|
+
if (!args.force) {
|
|
5950
|
+
const readline = await import("readline");
|
|
5951
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5952
|
+
const answer = await new Promise((resolve2) => {
|
|
5953
|
+
rl.question(`\u26A0\uFE0F Delete ${toRemove.length} observations? (y/N) `, resolve2);
|
|
5954
|
+
});
|
|
5955
|
+
rl.close();
|
|
5956
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
5957
|
+
console.log("\u274C Cancelled.");
|
|
5958
|
+
return;
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
const removeIds = new Set(toRemove.map((o) => JSON.stringify(o)));
|
|
5962
|
+
const remaining = allObs.filter((o) => !removeIds.has(JSON.stringify(o)));
|
|
5963
|
+
await saveObservationsJson(dataDir, remaining);
|
|
5964
|
+
console.log(`\u2705 Removed ${toRemove.length} observations. ${remaining.length} remaining.`);
|
|
5965
|
+
}
|
|
5966
|
+
});
|
|
5967
|
+
}
|
|
5968
|
+
});
|
|
5969
|
+
|
|
5753
5970
|
// src/cli/index.ts
|
|
5754
5971
|
init_esm_shims();
|
|
5755
|
-
import { defineCommand as
|
|
5972
|
+
import { defineCommand as defineCommand11, runMain } from "citty";
|
|
5756
5973
|
import { createRequire as createRequire2 } from "module";
|
|
5757
5974
|
var require2 = createRequire2(import.meta.url);
|
|
5758
5975
|
var pkg = require2("../../package.json");
|
|
5759
|
-
var main =
|
|
5976
|
+
var main = defineCommand11({
|
|
5760
5977
|
meta: {
|
|
5761
5978
|
name: "memorix",
|
|
5762
5979
|
version: pkg.version,
|
|
@@ -5768,7 +5985,8 @@ var main = defineCommand10({
|
|
|
5768
5985
|
sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
|
|
5769
5986
|
hook: () => Promise.resolve().then(() => (init_hook(), hook_exports)).then((m) => m.default),
|
|
5770
5987
|
hooks: () => Promise.resolve().then(() => (init_hooks(), hooks_exports)).then((m) => m.default),
|
|
5771
|
-
dashboard: () => Promise.resolve().then(() => (init_dashboard(), dashboard_exports)).then((m) => m.default)
|
|
5988
|
+
dashboard: () => Promise.resolve().then(() => (init_dashboard(), dashboard_exports)).then((m) => m.default),
|
|
5989
|
+
cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m) => m.default)
|
|
5772
5990
|
},
|
|
5773
5991
|
run() {
|
|
5774
5992
|
}
|