memorix 0.1.5 → 0.2.1

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 CHANGED
@@ -722,6 +722,15 @@ var init_entity_extractor = __esm({
722
722
  });
723
723
 
724
724
  // src/memory/observations.ts
725
+ var observations_exports = {};
726
+ __export(observations_exports, {
727
+ getObservation: () => getObservation,
728
+ getObservationCount: () => getObservationCount2,
729
+ getProjectObservations: () => getProjectObservations,
730
+ initObservations: () => initObservations,
731
+ reindexObservations: () => reindexObservations,
732
+ storeObservation: () => storeObservation
733
+ });
725
734
  async function initObservations(dir) {
726
735
  projectDir = dir;
727
736
  const loaded = await loadObservationsJson(dir);
@@ -793,6 +802,12 @@ async function storeObservation(input) {
793
802
  function getObservation(id) {
794
803
  return observations.find((o) => o.id === id);
795
804
  }
805
+ function getProjectObservations(projectId) {
806
+ return observations.filter((o) => o.projectId === projectId);
807
+ }
808
+ function getObservationCount2() {
809
+ return observations.length;
810
+ }
796
811
  async function reindexObservations() {
797
812
  let count2 = 0;
798
813
  for (const obs of observations) {
@@ -2264,10 +2279,10 @@ var init_engine2 = __esm({
2264
2279
  for (const [target, adapter] of this.adapters) {
2265
2280
  const configPath = adapter.getConfigPath(this.projectRoot);
2266
2281
  const globalPath = adapter.getConfigPath();
2267
- for (const path5 of [configPath, globalPath]) {
2268
- if (existsSync3(path5)) {
2282
+ for (const path6 of [configPath, globalPath]) {
2283
+ if (existsSync3(path6)) {
2269
2284
  try {
2270
- const content = readFileSync(path5, "utf-8");
2285
+ const content = readFileSync(path6, "utf-8");
2271
2286
  const servers = adapter.parse(content);
2272
2287
  if (servers.length > 0) {
2273
2288
  mcpConfigs[target] = servers;
@@ -2532,6 +2547,7 @@ var server_exports = {};
2532
2547
  __export(server_exports, {
2533
2548
  createMemorixServer: () => createMemorixServer
2534
2549
  });
2550
+ import { watch } from "fs";
2535
2551
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2536
2552
  import { z } from "zod";
2537
2553
  async function createMemorixServer(cwd) {
@@ -2546,6 +2562,26 @@ async function createMemorixServer(cwd) {
2546
2562
  }
2547
2563
  console.error(`[memorix] Project: ${project.id} (${project.name})`);
2548
2564
  console.error(`[memorix] Data dir: ${projectDir2}`);
2565
+ const observationsFile = projectDir2 + "/observations.json";
2566
+ let reloadDebounce = null;
2567
+ try {
2568
+ watch(observationsFile, () => {
2569
+ if (reloadDebounce) clearTimeout(reloadDebounce);
2570
+ reloadDebounce = setTimeout(async () => {
2571
+ try {
2572
+ await initObservations(projectDir2);
2573
+ const count2 = await reindexObservations();
2574
+ if (count2 > 0) {
2575
+ console.error(`[memorix] Hot-reloaded ${count2} observations (external write detected)`);
2576
+ }
2577
+ } catch {
2578
+ }
2579
+ }, 500);
2580
+ });
2581
+ console.error(`[memorix] Watching for external writes (hooks hot-reload enabled)`);
2582
+ } catch {
2583
+ console.error(`[memorix] Warning: could not watch observations file for hot-reload`);
2584
+ }
2549
2585
  const server = new McpServer({
2550
2586
  name: "memorix",
2551
2587
  version: "0.1.0"
@@ -3204,8 +3240,8 @@ var init_sync = __esm({
3204
3240
  run: async ({ args }) => {
3205
3241
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
3206
3242
  const { RulesSyncer: RulesSyncer2 } = await Promise.resolve().then(() => (init_syncer(), syncer_exports));
3207
- const { promises: fs3 } = await import("fs");
3208
- const path5 = await import("path");
3243
+ const { promises: fs4 } = await import("fs");
3244
+ const path6 = await import("path");
3209
3245
  p2.intro("memorix sync");
3210
3246
  const project = detectProject2();
3211
3247
  p2.log.info(`Project: ${project.name} (${project.id})`);
@@ -3273,9 +3309,9 @@ var init_sync = __esm({
3273
3309
  process.exit(0);
3274
3310
  }
3275
3311
  for (const file of files) {
3276
- const fullPath = path5.join(project.rootPath, file.filePath);
3277
- await fs3.mkdir(path5.dirname(fullPath), { recursive: true });
3278
- await fs3.writeFile(fullPath, file.content, "utf-8");
3312
+ const fullPath = path6.join(project.rootPath, file.filePath);
3313
+ await fs4.mkdir(path6.dirname(fullPath), { recursive: true });
3314
+ await fs4.writeFile(fullPath, file.content, "utf-8");
3279
3315
  p2.log.success(`Written: ${file.filePath}`);
3280
3316
  }
3281
3317
  p2.outro(`Synced ${files.length} rule(s) to ${target} format`);
@@ -3284,10 +3320,918 @@ var init_sync = __esm({
3284
3320
  }
3285
3321
  });
3286
3322
 
3323
+ // src/hooks/normalizer.ts
3324
+ function detectAgent(payload) {
3325
+ if ("agent_action_name" in payload) return "windsurf";
3326
+ if ("hook_event_name" in payload && "conversation_id" in payload) return "cursor";
3327
+ if ("hookEventName" in payload) {
3328
+ return "copilot";
3329
+ }
3330
+ if ("event_type" in payload) return "kiro";
3331
+ if ("hook_type" in payload) return "codex";
3332
+ return "claude";
3333
+ }
3334
+ function extractEventName(payload, agent) {
3335
+ switch (agent) {
3336
+ case "windsurf":
3337
+ return payload.agent_action_name ?? "";
3338
+ case "cursor":
3339
+ return payload.hook_event_name ?? "";
3340
+ case "copilot":
3341
+ case "claude":
3342
+ return payload.hookEventName ?? "";
3343
+ case "kiro":
3344
+ return payload.event_type ?? "";
3345
+ case "codex":
3346
+ return payload.hook_type ?? "";
3347
+ default:
3348
+ return "";
3349
+ }
3350
+ }
3351
+ function normalizeClaude(payload, event) {
3352
+ const result = {
3353
+ sessionId: payload.sessionId ?? "",
3354
+ cwd: payload.cwd ?? "",
3355
+ transcriptPath: payload.transcript_path
3356
+ };
3357
+ const toolName = payload.tool_name ?? "";
3358
+ if (toolName) {
3359
+ result.toolName = toolName;
3360
+ result.toolInput = payload.tool_input;
3361
+ result.toolResult = payload.tool_result;
3362
+ if (toolName === "write" || toolName === "edit" || toolName === "multi_edit") {
3363
+ const input = payload.tool_input;
3364
+ result.filePath = input?.file_path ?? input?.filePath;
3365
+ }
3366
+ }
3367
+ if (event === "user_prompt") {
3368
+ result.userPrompt = payload.prompt ?? "";
3369
+ }
3370
+ return result;
3371
+ }
3372
+ function normalizeWindsurf(payload, event) {
3373
+ const toolInfo = payload.tool_info ?? {};
3374
+ const result = {
3375
+ sessionId: payload.trajectory_id ?? "",
3376
+ cwd: ""
3377
+ };
3378
+ switch (event) {
3379
+ case "post_edit":
3380
+ result.filePath = toolInfo.file_path;
3381
+ if (Array.isArray(toolInfo.edits)) {
3382
+ result.edits = toolInfo.edits.map((e) => ({
3383
+ oldString: e.old_string ?? "",
3384
+ newString: e.new_string ?? ""
3385
+ }));
3386
+ }
3387
+ break;
3388
+ case "post_command":
3389
+ result.command = toolInfo.command_line;
3390
+ result.cwd = toolInfo.cwd ?? "";
3391
+ break;
3392
+ case "post_tool":
3393
+ result.toolName = toolInfo.mcp_tool_name;
3394
+ result.toolInput = toolInfo.mcp_tool_arguments;
3395
+ result.toolResult = toolInfo.mcp_result;
3396
+ break;
3397
+ case "user_prompt":
3398
+ result.userPrompt = toolInfo.user_prompt;
3399
+ break;
3400
+ case "post_response":
3401
+ result.aiResponse = toolInfo.response;
3402
+ break;
3403
+ }
3404
+ return result;
3405
+ }
3406
+ function normalizeCursor(payload, event) {
3407
+ const result = {
3408
+ sessionId: payload.conversation_id ?? "",
3409
+ cwd: payload.cwd ?? ""
3410
+ };
3411
+ const roots = payload.workspace_roots;
3412
+ if (roots?.length && !result.cwd) {
3413
+ result.cwd = roots[0];
3414
+ }
3415
+ switch (event) {
3416
+ case "user_prompt":
3417
+ result.userPrompt = payload.prompt ?? "";
3418
+ break;
3419
+ case "post_command":
3420
+ result.command = payload.command ?? "";
3421
+ break;
3422
+ case "post_edit":
3423
+ result.filePath = payload.file_path ?? "";
3424
+ break;
3425
+ }
3426
+ return result;
3427
+ }
3428
+ function normalizeHookInput(payload) {
3429
+ const agent = detectAgent(payload);
3430
+ const rawEventName = extractEventName(payload, agent);
3431
+ const event = EVENT_MAP[rawEventName] ?? "post_tool";
3432
+ const timestamp = payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
3433
+ let agentSpecific = {};
3434
+ switch (agent) {
3435
+ case "claude":
3436
+ case "copilot":
3437
+ agentSpecific = normalizeClaude(payload, event);
3438
+ break;
3439
+ case "windsurf":
3440
+ agentSpecific = normalizeWindsurf(payload, event);
3441
+ break;
3442
+ case "cursor":
3443
+ agentSpecific = normalizeCursor(payload, event);
3444
+ break;
3445
+ default:
3446
+ agentSpecific = { sessionId: "", cwd: "" };
3447
+ }
3448
+ return {
3449
+ event,
3450
+ agent,
3451
+ timestamp,
3452
+ sessionId: agentSpecific.sessionId ?? "",
3453
+ cwd: agentSpecific.cwd ?? "",
3454
+ raw: payload,
3455
+ ...agentSpecific
3456
+ };
3457
+ }
3458
+ var EVENT_MAP;
3459
+ var init_normalizer = __esm({
3460
+ "src/hooks/normalizer.ts"() {
3461
+ "use strict";
3462
+ init_esm_shims();
3463
+ EVENT_MAP = {
3464
+ // Claude Code / VS Code Copilot
3465
+ SessionStart: "session_start",
3466
+ UserPromptSubmit: "user_prompt",
3467
+ PreToolUse: "post_tool",
3468
+ // we handle pre as post for memory purposes
3469
+ PostToolUse: "post_tool",
3470
+ PreCompact: "pre_compact",
3471
+ Stop: "session_end",
3472
+ // Windsurf
3473
+ pre_user_prompt: "user_prompt",
3474
+ post_write_code: "post_edit",
3475
+ post_read_code: "post_tool",
3476
+ post_run_command: "post_command",
3477
+ pre_mcp_tool_use: "post_tool",
3478
+ post_mcp_tool_use: "post_tool",
3479
+ post_cascade_response: "post_response",
3480
+ // Cursor
3481
+ beforeSubmitPrompt: "user_prompt",
3482
+ beforeShellExecution: "post_command",
3483
+ beforeMCPExecution: "post_tool",
3484
+ afterFileEdit: "post_edit",
3485
+ stop: "session_end"
3486
+ };
3487
+ }
3488
+ });
3489
+
3490
+ // src/hooks/pattern-detector.ts
3491
+ function detectPatterns(content) {
3492
+ if (!content || content.length < MIN_CONTENT_LENGTH) {
3493
+ return [];
3494
+ }
3495
+ const results = [];
3496
+ for (const pattern of PATTERNS) {
3497
+ if (content.length < pattern.minLength) continue;
3498
+ const matchedKeywords = [];
3499
+ let matchCount = 0;
3500
+ for (const regex of pattern.keywords) {
3501
+ const matches = content.match(new RegExp(regex.source, regex.flags + "g"));
3502
+ if (matches) {
3503
+ matchCount += matches.length;
3504
+ matchedKeywords.push(...matches.map((m) => m.trim()));
3505
+ }
3506
+ }
3507
+ if (matchCount > 0) {
3508
+ const confidence = Math.min(1, pattern.baseConfidence + matchCount * 0.05);
3509
+ results.push({
3510
+ type: pattern.type,
3511
+ confidence,
3512
+ matchedKeywords: [...new Set(matchedKeywords)].slice(0, 5)
3513
+ });
3514
+ }
3515
+ }
3516
+ results.sort((a, b) => b.confidence - a.confidence);
3517
+ return results;
3518
+ }
3519
+ function detectBestPattern(content, minConfidence = 0.5) {
3520
+ const patterns = detectPatterns(content);
3521
+ if (patterns.length === 0) return null;
3522
+ if (patterns[0].confidence < minConfidence) return null;
3523
+ return patterns[0];
3524
+ }
3525
+ function patternToObservationType(pattern) {
3526
+ const map = {
3527
+ decision: "decision",
3528
+ error: "problem-solution",
3529
+ gotcha: "gotcha",
3530
+ configuration: "what-changed",
3531
+ learning: "discovery",
3532
+ implementation: "what-changed"
3533
+ };
3534
+ return map[pattern] ?? "discovery";
3535
+ }
3536
+ var MIN_CONTENT_LENGTH, PATTERNS;
3537
+ var init_pattern_detector = __esm({
3538
+ "src/hooks/pattern-detector.ts"() {
3539
+ "use strict";
3540
+ init_esm_shims();
3541
+ MIN_CONTENT_LENGTH = 100;
3542
+ PATTERNS = [
3543
+ {
3544
+ type: "decision",
3545
+ keywords: [
3546
+ /\b(decided|chose|will use|settled on|going with|picked|selected)\b/i,
3547
+ /(决定|选择了|采用|确定用|最终选了)/,
3548
+ /\b(architecture|approach|strategy|pattern|framework)\b/i
3549
+ ],
3550
+ minLength: 100,
3551
+ baseConfidence: 0.8
3552
+ },
3553
+ {
3554
+ type: "error",
3555
+ keywords: [
3556
+ /\b(error|bug|fix(ed)?|resolv(ed|ing)|crash|fail(ed|ure)?|broken)\b/i,
3557
+ /(错误|修复|报错|崩溃|失败|异常|解决了)/,
3558
+ /\b(workaround|hotfix|patch|regression|stack\s*trace)\b/i
3559
+ ],
3560
+ minLength: 100,
3561
+ baseConfidence: 0.75
3562
+ },
3563
+ {
3564
+ type: "gotcha",
3565
+ keywords: [
3566
+ /\b(gotcha|pitfall|trap|caveat|watch out|careful|beware|warning)\b/i,
3567
+ /(坑|注意|陷阱|小心|踩坑|坑点)/,
3568
+ /\b(don'?t|never|avoid|must not|不要|千万别|切记)\b/i
3569
+ ],
3570
+ minLength: 80,
3571
+ baseConfidence: 0.85
3572
+ },
3573
+ {
3574
+ type: "configuration",
3575
+ keywords: [
3576
+ /\b(config(ured?|uration)?|setting|environment|env\b|\.env|path|port)\b/i,
3577
+ /(配置|环境|路径|端口|设置|安装)/,
3578
+ /\b(gradle|webpack|vite|tsconfig|package\.json|docker)\b/i
3579
+ ],
3580
+ minLength: 80,
3581
+ baseConfidence: 0.7
3582
+ },
3583
+ {
3584
+ type: "learning",
3585
+ keywords: [
3586
+ /\b(learn(ed)?|discover(ed)?|realiz(ed)?|turns?\s*out|found\s*out|TIL)\b/i,
3587
+ /(学到|发现|原来|才知道|了解到)/,
3588
+ /\b(insight|understanding|clarif(ied|ication))\b/i
3589
+ ],
3590
+ minLength: 150,
3591
+ baseConfidence: 0.65
3592
+ },
3593
+ {
3594
+ type: "implementation",
3595
+ keywords: [
3596
+ /\b(implement(ed)?|creat(ed|ing)|built|added|integrat(ed|ing))\b/i,
3597
+ /(实现了|创建了|添加了|集成了|完成了)/,
3598
+ /\b(refactor(ed)?|migrat(ed|ing)|upgrad(ed|ing))\b/i
3599
+ ],
3600
+ minLength: 200,
3601
+ baseConfidence: 0.5
3602
+ }
3603
+ ];
3604
+ }
3605
+ });
3606
+
3607
+ // src/hooks/handler.ts
3608
+ var handler_exports = {};
3609
+ __export(handler_exports, {
3610
+ handleHookEvent: () => handleHookEvent,
3611
+ runHook: () => runHook
3612
+ });
3613
+ function isInCooldown(eventKey) {
3614
+ const last = cooldowns.get(eventKey);
3615
+ if (!last) return false;
3616
+ return Date.now() - last < COOLDOWN_MS;
3617
+ }
3618
+ function markTriggered(eventKey) {
3619
+ cooldowns.set(eventKey, Date.now());
3620
+ }
3621
+ function extractContent(input) {
3622
+ const parts = [];
3623
+ if (input.userPrompt) parts.push(input.userPrompt);
3624
+ if (input.aiResponse) parts.push(input.aiResponse);
3625
+ if (input.toolResult) parts.push(input.toolResult);
3626
+ if (input.commandOutput) parts.push(input.commandOutput);
3627
+ if (input.command) parts.push(`Command: ${input.command}`);
3628
+ if (input.filePath) parts.push(`File: ${input.filePath}`);
3629
+ if (input.edits) {
3630
+ for (const edit of input.edits) {
3631
+ parts.push(`Edit: ${edit.oldString} \u2192 ${edit.newString}`);
3632
+ }
3633
+ }
3634
+ return parts.join("\n").slice(0, MAX_CONTENT_LENGTH);
3635
+ }
3636
+ function deriveEntityName(input) {
3637
+ if (input.filePath) {
3638
+ const parts = input.filePath.replace(/\\/g, "/").split("/");
3639
+ const filename = parts[parts.length - 1];
3640
+ return filename.replace(/\.[^.]+$/, "");
3641
+ }
3642
+ if (input.toolName) return input.toolName;
3643
+ if (input.command) {
3644
+ const firstWord = input.command.split(/\s+/)[0];
3645
+ return firstWord.replace(/[^a-zA-Z0-9-_]/g, "");
3646
+ }
3647
+ return "session";
3648
+ }
3649
+ function generateTitle(input, patternType) {
3650
+ const maxLen = 60;
3651
+ if (input.filePath) {
3652
+ const filename = input.filePath.replace(/\\/g, "/").split("/").pop() ?? "";
3653
+ const verb = patternType === "problem-solution" ? "Fixed issue in" : patternType === "what-changed" ? "Changed" : "Updated";
3654
+ return `${verb} ${filename}`.slice(0, maxLen);
3655
+ }
3656
+ if (input.command) {
3657
+ return `Ran: ${input.command}`.slice(0, maxLen);
3658
+ }
3659
+ if (input.userPrompt) {
3660
+ return input.userPrompt.slice(0, maxLen);
3661
+ }
3662
+ return `Session activity (${patternType})`;
3663
+ }
3664
+ function buildObservation(input, content) {
3665
+ const pattern = detectBestPattern(content);
3666
+ const obsType = pattern ? patternToObservationType(pattern.type) : "discovery";
3667
+ return {
3668
+ entityName: deriveEntityName(input),
3669
+ type: obsType,
3670
+ title: generateTitle(input, obsType),
3671
+ narrative: content.slice(0, 2e3),
3672
+ facts: [
3673
+ `Agent: ${input.agent}`,
3674
+ `Session: ${input.sessionId}`,
3675
+ ...input.filePath ? [`File: ${input.filePath}`] : [],
3676
+ ...input.command ? [`Command: ${input.command}`] : []
3677
+ ],
3678
+ concepts: pattern?.matchedKeywords ?? [],
3679
+ filesModified: input.filePath ? [input.filePath] : []
3680
+ };
3681
+ }
3682
+ async function handleHookEvent(input) {
3683
+ const defaultOutput = { continue: true };
3684
+ if (input.toolName === "memorix_store" || input.toolName === "memorix_search") {
3685
+ return { observation: null, output: defaultOutput };
3686
+ }
3687
+ switch (input.event) {
3688
+ case "session_start":
3689
+ return {
3690
+ observation: null,
3691
+ output: {
3692
+ continue: true,
3693
+ systemMessage: "Memorix is active. Your memories from previous sessions are available via memorix_search."
3694
+ }
3695
+ };
3696
+ case "pre_compact":
3697
+ return {
3698
+ observation: buildObservation(input, extractContent(input)),
3699
+ output: defaultOutput
3700
+ };
3701
+ case "session_end":
3702
+ return {
3703
+ observation: buildObservation(input, extractContent(input)),
3704
+ output: defaultOutput
3705
+ };
3706
+ case "post_edit":
3707
+ case "post_command":
3708
+ case "post_tool":
3709
+ case "post_response":
3710
+ case "user_prompt": {
3711
+ const cooldownKey = `${input.event}:${input.filePath ?? input.command ?? "general"}`;
3712
+ if (isInCooldown(cooldownKey)) {
3713
+ return { observation: null, output: defaultOutput };
3714
+ }
3715
+ const content = extractContent(input);
3716
+ if (content.length < MIN_STORE_LENGTH) {
3717
+ return { observation: null, output: defaultOutput };
3718
+ }
3719
+ const pattern = detectBestPattern(content);
3720
+ if (!pattern) {
3721
+ return { observation: null, output: defaultOutput };
3722
+ }
3723
+ markTriggered(cooldownKey);
3724
+ return {
3725
+ observation: buildObservation(input, content),
3726
+ output: defaultOutput
3727
+ };
3728
+ }
3729
+ default:
3730
+ return { observation: null, output: defaultOutput };
3731
+ }
3732
+ }
3733
+ async function runHook() {
3734
+ const chunks = [];
3735
+ for await (const chunk of process.stdin) {
3736
+ chunks.push(chunk);
3737
+ }
3738
+ const rawInput = Buffer.concat(chunks).toString("utf-8").trim();
3739
+ if (!rawInput) {
3740
+ process.stdout.write(JSON.stringify({ continue: true }));
3741
+ return;
3742
+ }
3743
+ let payload;
3744
+ try {
3745
+ payload = JSON.parse(rawInput);
3746
+ } catch {
3747
+ process.stdout.write(JSON.stringify({ continue: true }));
3748
+ return;
3749
+ }
3750
+ const input = normalizeHookInput(payload);
3751
+ const { observation, output } = await handleHookEvent(input);
3752
+ if (observation) {
3753
+ try {
3754
+ const { storeObservation: storeObservation2, initObservations: initObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
3755
+ const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
3756
+ const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
3757
+ const project = await detectProject2(input.cwd || process.cwd());
3758
+ const dataDir = await getProjectDataDir2(project.id);
3759
+ await initObservations2(dataDir);
3760
+ await storeObservation2({ ...observation, projectId: project.id });
3761
+ } catch {
3762
+ }
3763
+ }
3764
+ process.stdout.write(JSON.stringify(output));
3765
+ }
3766
+ var cooldowns, COOLDOWN_MS, MIN_STORE_LENGTH, MAX_CONTENT_LENGTH;
3767
+ var init_handler = __esm({
3768
+ "src/hooks/handler.ts"() {
3769
+ "use strict";
3770
+ init_esm_shims();
3771
+ init_normalizer();
3772
+ init_pattern_detector();
3773
+ cooldowns = /* @__PURE__ */ new Map();
3774
+ COOLDOWN_MS = 3e4;
3775
+ MIN_STORE_LENGTH = 100;
3776
+ MAX_CONTENT_LENGTH = 4e3;
3777
+ }
3778
+ });
3779
+
3780
+ // src/cli/commands/hook.ts
3781
+ var hook_exports = {};
3782
+ __export(hook_exports, {
3783
+ default: () => hook_default
3784
+ });
3785
+ import { defineCommand as defineCommand4 } from "citty";
3786
+ var hook_default;
3787
+ var init_hook = __esm({
3788
+ "src/cli/commands/hook.ts"() {
3789
+ "use strict";
3790
+ init_esm_shims();
3791
+ hook_default = defineCommand4({
3792
+ meta: {
3793
+ name: "hook",
3794
+ description: "Handle agent hook event (called by agent hook configs)"
3795
+ },
3796
+ run: async () => {
3797
+ const { runHook: runHook2 } = await Promise.resolve().then(() => (init_handler(), handler_exports));
3798
+ await runHook2();
3799
+ }
3800
+ });
3801
+ }
3802
+ });
3803
+
3804
+ // src/hooks/installers/index.ts
3805
+ var installers_exports = {};
3806
+ __export(installers_exports, {
3807
+ detectInstalledAgents: () => detectInstalledAgents,
3808
+ getHookStatus: () => getHookStatus,
3809
+ installHooks: () => installHooks,
3810
+ uninstallHooks: () => uninstallHooks
3811
+ });
3812
+ import * as fs3 from "fs/promises";
3813
+ import * as path5 from "path";
3814
+ import * as os2 from "os";
3815
+ function generateClaudeConfig() {
3816
+ const hookEntry = {
3817
+ type: "command",
3818
+ command: `${HOOK_COMMAND} ${HOOK_ARGS.join(" ")}`,
3819
+ timeout: 10
3820
+ };
3821
+ return {
3822
+ hooks: {
3823
+ SessionStart: [hookEntry],
3824
+ PostToolUse: [hookEntry],
3825
+ UserPromptSubmit: [hookEntry],
3826
+ PreCompact: [hookEntry],
3827
+ Stop: [hookEntry]
3828
+ }
3829
+ };
3830
+ }
3831
+ function generateWindsurfConfig() {
3832
+ const hookEntry = {
3833
+ command: `${HOOK_COMMAND} ${HOOK_ARGS.join(" ")}`,
3834
+ timeout: 10
3835
+ };
3836
+ return {
3837
+ hooks: {
3838
+ post_write_code: [hookEntry],
3839
+ post_run_command: [hookEntry],
3840
+ post_mcp_tool_use: [hookEntry],
3841
+ pre_user_prompt: [hookEntry],
3842
+ post_cascade_response: [hookEntry]
3843
+ }
3844
+ };
3845
+ }
3846
+ function generateCursorConfig() {
3847
+ return {
3848
+ hooks: {
3849
+ beforeSubmitPrompt: {
3850
+ command: `${HOOK_COMMAND} ${HOOK_ARGS.join(" ")}`
3851
+ },
3852
+ afterFileEdit: {
3853
+ command: `${HOOK_COMMAND} ${HOOK_ARGS.join(" ")}`
3854
+ },
3855
+ stop: {
3856
+ command: `${HOOK_COMMAND} ${HOOK_ARGS.join(" ")}`
3857
+ }
3858
+ }
3859
+ };
3860
+ }
3861
+ function generateKiroHookFile() {
3862
+ return `---
3863
+ title: Memorix Auto-Memory
3864
+ description: Automatically record development context for cross-agent memory sharing
3865
+ event: file_saved
3866
+ filePattern: "**/*"
3867
+ ---
3868
+
3869
+ Run the memorix hook command to analyze changes and store relevant memories:
3870
+
3871
+ \`\`\`bash
3872
+ memorix hook
3873
+ \`\`\`
3874
+ `;
3875
+ }
3876
+ function getProjectConfigPath(agent, projectRoot) {
3877
+ switch (agent) {
3878
+ case "claude":
3879
+ case "copilot":
3880
+ return path5.join(projectRoot, ".github", "hooks", "memorix.json");
3881
+ case "windsurf":
3882
+ return path5.join(projectRoot, ".windsurf", "hooks.json");
3883
+ case "cursor":
3884
+ return path5.join(projectRoot, ".cursor", "hooks.json");
3885
+ case "kiro":
3886
+ return path5.join(projectRoot, ".kiro", "hooks", "memorix.hook.md");
3887
+ case "codex":
3888
+ return path5.join(projectRoot, ".codex", "hooks.json");
3889
+ default:
3890
+ return path5.join(projectRoot, ".memorix", "hooks.json");
3891
+ }
3892
+ }
3893
+ function getGlobalConfigPath(agent) {
3894
+ const home = os2.homedir();
3895
+ switch (agent) {
3896
+ case "claude":
3897
+ case "copilot":
3898
+ return path5.join(home, ".claude", "settings.json");
3899
+ case "windsurf":
3900
+ return path5.join(home, ".codeium", "windsurf", "hooks.json");
3901
+ case "cursor":
3902
+ return path5.join(home, ".cursor", "hooks.json");
3903
+ default:
3904
+ return path5.join(home, ".memorix", "hooks.json");
3905
+ }
3906
+ }
3907
+ async function detectInstalledAgents() {
3908
+ const agents = [];
3909
+ const home = os2.homedir();
3910
+ const claudeDir = path5.join(home, ".claude");
3911
+ try {
3912
+ await fs3.access(claudeDir);
3913
+ agents.push("claude");
3914
+ } catch {
3915
+ }
3916
+ const windsurfDir = path5.join(home, ".codeium", "windsurf");
3917
+ try {
3918
+ await fs3.access(windsurfDir);
3919
+ agents.push("windsurf");
3920
+ } catch {
3921
+ }
3922
+ const cursorDir = path5.join(home, ".cursor");
3923
+ try {
3924
+ await fs3.access(cursorDir);
3925
+ agents.push("cursor");
3926
+ } catch {
3927
+ }
3928
+ if (!agents.includes("claude")) {
3929
+ const vscodeDir = path5.join(home, ".vscode");
3930
+ try {
3931
+ await fs3.access(vscodeDir);
3932
+ agents.push("copilot");
3933
+ } catch {
3934
+ }
3935
+ }
3936
+ const kiroConfig = path5.join(home, ".kiro");
3937
+ try {
3938
+ await fs3.access(kiroConfig);
3939
+ agents.push("kiro");
3940
+ } catch {
3941
+ }
3942
+ return agents;
3943
+ }
3944
+ async function installHooks(agent, projectRoot, global = false) {
3945
+ const configPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
3946
+ let generated;
3947
+ switch (agent) {
3948
+ case "claude":
3949
+ case "copilot":
3950
+ generated = generateClaudeConfig();
3951
+ break;
3952
+ case "windsurf":
3953
+ generated = generateWindsurfConfig();
3954
+ break;
3955
+ case "cursor":
3956
+ generated = generateCursorConfig();
3957
+ break;
3958
+ case "kiro":
3959
+ generated = generateKiroHookFile();
3960
+ break;
3961
+ default:
3962
+ generated = generateClaudeConfig();
3963
+ }
3964
+ await fs3.mkdir(path5.dirname(configPath), { recursive: true });
3965
+ if (agent === "kiro") {
3966
+ await fs3.writeFile(configPath, generated, "utf-8");
3967
+ } else {
3968
+ let existing = {};
3969
+ try {
3970
+ const content = await fs3.readFile(configPath, "utf-8");
3971
+ existing = JSON.parse(content);
3972
+ } catch {
3973
+ }
3974
+ const merged = {
3975
+ ...existing,
3976
+ ...generated
3977
+ };
3978
+ await fs3.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
3979
+ }
3980
+ const events = [];
3981
+ switch (agent) {
3982
+ case "claude":
3983
+ case "copilot":
3984
+ events.push("session_start", "post_tool", "user_prompt", "pre_compact", "session_end");
3985
+ break;
3986
+ case "windsurf":
3987
+ events.push("post_edit", "post_command", "post_tool", "user_prompt", "post_response");
3988
+ break;
3989
+ case "cursor":
3990
+ events.push("user_prompt", "post_edit", "session_end");
3991
+ break;
3992
+ case "kiro":
3993
+ events.push("post_edit");
3994
+ break;
3995
+ }
3996
+ return {
3997
+ agent,
3998
+ configPath,
3999
+ events,
4000
+ generated: typeof generated === "string" ? { content: generated } : generated
4001
+ };
4002
+ }
4003
+ async function uninstallHooks(agent, projectRoot, global = false) {
4004
+ const configPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
4005
+ try {
4006
+ if (agent === "kiro") {
4007
+ await fs3.unlink(configPath);
4008
+ } else {
4009
+ const content = await fs3.readFile(configPath, "utf-8");
4010
+ const config = JSON.parse(content);
4011
+ delete config.hooks;
4012
+ if (Object.keys(config).length === 0) {
4013
+ await fs3.unlink(configPath);
4014
+ } else {
4015
+ await fs3.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
4016
+ }
4017
+ }
4018
+ return true;
4019
+ } catch {
4020
+ return false;
4021
+ }
4022
+ }
4023
+ async function getHookStatus(projectRoot) {
4024
+ const results = [];
4025
+ const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex"];
4026
+ for (const agent of agents) {
4027
+ const projectPath = getProjectConfigPath(agent, projectRoot);
4028
+ const globalPath = getGlobalConfigPath(agent);
4029
+ let installed = false;
4030
+ let usedPath = projectPath;
4031
+ try {
4032
+ await fs3.access(projectPath);
4033
+ installed = true;
4034
+ } catch {
4035
+ try {
4036
+ await fs3.access(globalPath);
4037
+ installed = true;
4038
+ usedPath = globalPath;
4039
+ } catch {
4040
+ }
4041
+ }
4042
+ results.push({ agent, installed, configPath: usedPath });
4043
+ }
4044
+ return results;
4045
+ }
4046
+ var HOOK_COMMAND, HOOK_ARGS;
4047
+ var init_installers = __esm({
4048
+ "src/hooks/installers/index.ts"() {
4049
+ "use strict";
4050
+ init_esm_shims();
4051
+ HOOK_COMMAND = "memorix";
4052
+ HOOK_ARGS = ["hook"];
4053
+ }
4054
+ });
4055
+
4056
+ // src/cli/commands/hooks-install.ts
4057
+ var hooks_install_exports = {};
4058
+ __export(hooks_install_exports, {
4059
+ default: () => hooks_install_default
4060
+ });
4061
+ import { defineCommand as defineCommand5 } from "citty";
4062
+ var hooks_install_default;
4063
+ var init_hooks_install = __esm({
4064
+ "src/cli/commands/hooks-install.ts"() {
4065
+ "use strict";
4066
+ init_esm_shims();
4067
+ hooks_install_default = defineCommand5({
4068
+ meta: {
4069
+ name: "install",
4070
+ description: "Install automatic memory hooks for agents"
4071
+ },
4072
+ args: {
4073
+ agent: {
4074
+ type: "string",
4075
+ description: "Target agent (claude|copilot|windsurf|cursor|kiro|codex). Auto-detects if omitted.",
4076
+ required: false
4077
+ },
4078
+ global: {
4079
+ type: "boolean",
4080
+ description: "Install globally instead of per-project",
4081
+ required: false
4082
+ }
4083
+ },
4084
+ run: async ({ args }) => {
4085
+ const { detectInstalledAgents: detectInstalledAgents2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
4086
+ const cwd = process.cwd();
4087
+ let agents;
4088
+ if (args.agent) {
4089
+ agents = [args.agent];
4090
+ } else {
4091
+ agents = await detectInstalledAgents2();
4092
+ if (agents.length === 0) {
4093
+ console.log("No supported agents detected. Use --agent to specify one.");
4094
+ return;
4095
+ }
4096
+ console.log(`Detected agents: ${agents.join(", ")}`);
4097
+ }
4098
+ for (const agent of agents) {
4099
+ try {
4100
+ const config = await installHooks2(
4101
+ agent,
4102
+ cwd,
4103
+ args.global ?? false
4104
+ );
4105
+ console.log(`\u2705 ${agent}: hooks installed \u2192 ${config.configPath}`);
4106
+ console.log(` Events: ${config.events.join(", ")}`);
4107
+ } catch (err) {
4108
+ console.error(`\u274C ${agent}: failed \u2014 ${err}`);
4109
+ }
4110
+ }
4111
+ console.log("\nMemory hooks are now active. Restart your agent to apply.");
4112
+ }
4113
+ });
4114
+ }
4115
+ });
4116
+
4117
+ // src/cli/commands/hooks-uninstall.ts
4118
+ var hooks_uninstall_exports = {};
4119
+ __export(hooks_uninstall_exports, {
4120
+ default: () => hooks_uninstall_default
4121
+ });
4122
+ import { defineCommand as defineCommand6 } from "citty";
4123
+ var hooks_uninstall_default;
4124
+ var init_hooks_uninstall = __esm({
4125
+ "src/cli/commands/hooks-uninstall.ts"() {
4126
+ "use strict";
4127
+ init_esm_shims();
4128
+ hooks_uninstall_default = defineCommand6({
4129
+ meta: {
4130
+ name: "uninstall",
4131
+ description: "Remove automatic memory hooks for agents"
4132
+ },
4133
+ args: {
4134
+ agent: {
4135
+ type: "string",
4136
+ description: "Target agent (claude|copilot|windsurf|cursor|kiro|codex)",
4137
+ required: false
4138
+ },
4139
+ global: {
4140
+ type: "boolean",
4141
+ description: "Uninstall global hooks",
4142
+ required: false
4143
+ }
4144
+ },
4145
+ run: async ({ args }) => {
4146
+ const { detectInstalledAgents: detectInstalledAgents2, uninstallHooks: uninstallHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
4147
+ const cwd = process.cwd();
4148
+ let agents;
4149
+ if (args.agent) {
4150
+ agents = [args.agent];
4151
+ } else {
4152
+ agents = await detectInstalledAgents2();
4153
+ }
4154
+ for (const agent of agents) {
4155
+ const ok = await uninstallHooks2(
4156
+ agent,
4157
+ cwd,
4158
+ args.global ?? false
4159
+ );
4160
+ if (ok) {
4161
+ console.log(`\u2705 ${agent}: hooks removed`);
4162
+ } else {
4163
+ console.log(`\u26A0\uFE0F ${agent}: no hooks found`);
4164
+ }
4165
+ }
4166
+ }
4167
+ });
4168
+ }
4169
+ });
4170
+
4171
+ // src/cli/commands/hooks-status.ts
4172
+ var hooks_status_exports = {};
4173
+ __export(hooks_status_exports, {
4174
+ default: () => hooks_status_default
4175
+ });
4176
+ import { defineCommand as defineCommand7 } from "citty";
4177
+ var hooks_status_default;
4178
+ var init_hooks_status = __esm({
4179
+ "src/cli/commands/hooks-status.ts"() {
4180
+ "use strict";
4181
+ init_esm_shims();
4182
+ hooks_status_default = defineCommand7({
4183
+ meta: {
4184
+ name: "status",
4185
+ description: "Show hook installation status for all agents"
4186
+ },
4187
+ run: async () => {
4188
+ const { getHookStatus: getHookStatus2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
4189
+ const cwd = process.cwd();
4190
+ const statuses = await getHookStatus2(cwd);
4191
+ console.log("\nMemorix Hooks Status");
4192
+ console.log("\u2550".repeat(50));
4193
+ for (const { agent, installed, configPath } of statuses) {
4194
+ const icon = installed ? "\u2705" : "\u2B1A";
4195
+ const label = agent.charAt(0).toUpperCase() + agent.slice(1);
4196
+ console.log(`${icon} ${label.padEnd(12)} ${installed ? configPath : "(not installed)"}`);
4197
+ }
4198
+ console.log("\nRun `memorix hooks install` to set up hooks for detected agents.");
4199
+ }
4200
+ });
4201
+ }
4202
+ });
4203
+
4204
+ // src/cli/commands/hooks.ts
4205
+ var hooks_exports = {};
4206
+ __export(hooks_exports, {
4207
+ default: () => hooks_default
4208
+ });
4209
+ import { defineCommand as defineCommand8 } from "citty";
4210
+ var hooks_default;
4211
+ var init_hooks = __esm({
4212
+ "src/cli/commands/hooks.ts"() {
4213
+ "use strict";
4214
+ init_esm_shims();
4215
+ hooks_default = defineCommand8({
4216
+ meta: {
4217
+ name: "hooks",
4218
+ description: "Manage automatic memory hooks for agents"
4219
+ },
4220
+ subCommands: {
4221
+ install: () => Promise.resolve().then(() => (init_hooks_install(), hooks_install_exports)).then((m) => m.default),
4222
+ uninstall: () => Promise.resolve().then(() => (init_hooks_uninstall(), hooks_uninstall_exports)).then((m) => m.default),
4223
+ status: () => Promise.resolve().then(() => (init_hooks_status(), hooks_status_exports)).then((m) => m.default)
4224
+ },
4225
+ run() {
4226
+ }
4227
+ });
4228
+ }
4229
+ });
4230
+
3287
4231
  // src/cli/index.ts
3288
4232
  init_esm_shims();
3289
- import { defineCommand as defineCommand4, runMain } from "citty";
3290
- var main = defineCommand4({
4233
+ import { defineCommand as defineCommand9, runMain } from "citty";
4234
+ var main = defineCommand9({
3291
4235
  meta: {
3292
4236
  name: "memorix",
3293
4237
  version: "0.1.0",
@@ -3296,7 +4240,9 @@ var main = defineCommand4({
3296
4240
  subCommands: {
3297
4241
  serve: () => Promise.resolve().then(() => (init_serve(), serve_exports)).then((m) => m.default),
3298
4242
  status: () => Promise.resolve().then(() => (init_status(), status_exports)).then((m) => m.default),
3299
- sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default)
4243
+ sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
4244
+ hook: () => Promise.resolve().then(() => (init_hook(), hook_exports)).then((m) => m.default),
4245
+ hooks: () => Promise.resolve().then(() => (init_hooks(), hooks_exports)).then((m) => m.default)
3300
4246
  },
3301
4247
  run() {
3302
4248
  }