memorix 0.9.37 → 0.10.0

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
@@ -39032,6 +39032,86 @@ var init_kiro2 = __esm({
39032
39032
  }
39033
39033
  });
39034
39034
 
39035
+ // src/workspace/mcp-adapters/opencode.ts
39036
+ import { homedir as homedir9 } from "os";
39037
+ import { join as join9 } from "path";
39038
+ var OpenCodeMCPAdapter;
39039
+ var init_opencode = __esm({
39040
+ "src/workspace/mcp-adapters/opencode.ts"() {
39041
+ "use strict";
39042
+ init_esm_shims();
39043
+ OpenCodeMCPAdapter = class {
39044
+ source = "opencode";
39045
+ parse(content) {
39046
+ try {
39047
+ const config2 = JSON.parse(content);
39048
+ const servers = config2.mcp ?? {};
39049
+ return Object.entries(servers).map(([name, entry]) => {
39050
+ const result = {
39051
+ name,
39052
+ command: "",
39053
+ args: []
39054
+ };
39055
+ if (entry.type === "remote" && entry.url) {
39056
+ result.url = entry.url;
39057
+ if (entry.headers && typeof entry.headers === "object" && Object.keys(entry.headers).length > 0) {
39058
+ result.headers = entry.headers;
39059
+ }
39060
+ } else {
39061
+ if (Array.isArray(entry.command) && entry.command.length > 0) {
39062
+ result.command = entry.command[0];
39063
+ result.args = entry.command.slice(1);
39064
+ } else if (typeof entry.command === "string") {
39065
+ result.command = entry.command;
39066
+ }
39067
+ }
39068
+ const env2 = entry.environment ?? entry.env;
39069
+ if (env2 && typeof env2 === "object" && Object.keys(env2).length > 0) {
39070
+ result.env = env2;
39071
+ }
39072
+ if (entry.enabled === false) {
39073
+ result.disabled = true;
39074
+ }
39075
+ return result;
39076
+ });
39077
+ } catch {
39078
+ return [];
39079
+ }
39080
+ }
39081
+ generate(servers) {
39082
+ const mcp = {};
39083
+ for (const s2 of servers) {
39084
+ const entry = {};
39085
+ if (s2.url) {
39086
+ entry.type = "remote";
39087
+ entry.url = s2.url;
39088
+ if (s2.headers && Object.keys(s2.headers).length > 0) {
39089
+ entry.headers = s2.headers;
39090
+ }
39091
+ } else {
39092
+ entry.type = "local";
39093
+ entry.command = [s2.command, ...s2.args];
39094
+ }
39095
+ if (s2.env && Object.keys(s2.env).length > 0) {
39096
+ entry.environment = s2.env;
39097
+ }
39098
+ if (s2.disabled === true) {
39099
+ entry.enabled = false;
39100
+ }
39101
+ mcp[s2.name] = entry;
39102
+ }
39103
+ return JSON.stringify({ $schema: "https://opencode.ai/config.json", mcp }, null, 2);
39104
+ }
39105
+ getConfigPath(projectRoot) {
39106
+ if (projectRoot) {
39107
+ return join9(projectRoot, "opencode.json");
39108
+ }
39109
+ return join9(homedir9(), ".config", "opencode", "opencode.json");
39110
+ }
39111
+ };
39112
+ }
39113
+ });
39114
+
39035
39115
  // src/workspace/workflow-sync.ts
39036
39116
  var import_gray_matter8, WorkflowSyncer;
39037
39117
  var init_workflow_sync = __esm({
@@ -39275,8 +39355,8 @@ var init_applier = __esm({
39275
39355
 
39276
39356
  // src/workspace/engine.ts
39277
39357
  import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync3, cpSync, mkdirSync as mkdirSync2 } from "fs";
39278
- import { join as join10 } from "path";
39279
- import { homedir as homedir9 } from "os";
39358
+ import { join as join11 } from "path";
39359
+ import { homedir as homedir10 } from "os";
39280
39360
  var WorkspaceSyncEngine;
39281
39361
  var init_engine2 = __esm({
39282
39362
  "src/workspace/engine.ts"() {
@@ -39289,6 +39369,7 @@ var init_engine2 = __esm({
39289
39369
  init_copilot2();
39290
39370
  init_antigravity2();
39291
39371
  init_kiro2();
39372
+ init_opencode();
39292
39373
  init_workflow_sync();
39293
39374
  init_syncer();
39294
39375
  init_sanitizer();
@@ -39303,7 +39384,8 @@ var init_engine2 = __esm({
39303
39384
  ["claude-code", new ClaudeCodeMCPAdapter()],
39304
39385
  ["copilot", new CopilotMCPAdapter()],
39305
39386
  ["antigravity", new AntigravityMCPAdapter()],
39306
- ["kiro", new KiroMCPAdapter()]
39387
+ ["kiro", new KiroMCPAdapter()],
39388
+ ["opencode", new OpenCodeMCPAdapter()]
39307
39389
  ]);
39308
39390
  this.workflowSyncer = new WorkflowSyncer();
39309
39391
  this.rulesSyncer = new RulesSyncer(projectRoot);
@@ -39322,7 +39404,8 @@ var init_engine2 = __esm({
39322
39404
  "claude-code": [],
39323
39405
  copilot: [],
39324
39406
  antigravity: [],
39325
- kiro: []
39407
+ kiro: [],
39408
+ opencode: []
39326
39409
  };
39327
39410
  for (const [target, adapter] of this.adapters) {
39328
39411
  const configPath = adapter.getConfigPath(this.projectRoot);
@@ -39427,13 +39510,14 @@ var init_engine2 = __esm({
39427
39510
  "claude-code": [".claude/skills"],
39428
39511
  copilot: [".github/skills", ".copilot/skills"],
39429
39512
  antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"],
39430
- kiro: [".kiro/skills"]
39513
+ kiro: [".kiro/skills"],
39514
+ opencode: [".opencode/skills"]
39431
39515
  };
39432
39516
  /** Get the target skills directory for an agent (null if agent has no skills support) */
39433
39517
  getTargetSkillsDir(target) {
39434
39518
  const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
39435
39519
  if (!dirs || dirs.length === 0) return null;
39436
- return join10(this.projectRoot, dirs[0]);
39520
+ return join11(this.projectRoot, dirs[0]);
39437
39521
  }
39438
39522
  /**
39439
39523
  * Scan all agent skills directories and collect unique skills.
@@ -39442,12 +39526,12 @@ var init_engine2 = __esm({
39442
39526
  const skills = [];
39443
39527
  const conflicts = [];
39444
39528
  const seen = /* @__PURE__ */ new Map();
39445
- const home = homedir9();
39529
+ const home = homedir10();
39446
39530
  for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
39447
39531
  for (const dir of dirs) {
39448
39532
  const paths = [
39449
- join10(this.projectRoot, dir),
39450
- join10(home, dir)
39533
+ join11(this.projectRoot, dir),
39534
+ join11(home, dir)
39451
39535
  ];
39452
39536
  for (const skillsRoot of paths) {
39453
39537
  if (!existsSync3(skillsRoot)) continue;
@@ -39455,7 +39539,7 @@ var init_engine2 = __esm({
39455
39539
  const entries = readdirSync(skillsRoot, { withFileTypes: true });
39456
39540
  for (const entry of entries) {
39457
39541
  if (!entry.isDirectory()) continue;
39458
- const skillMd = join10(skillsRoot, entry.name, "SKILL.md");
39542
+ const skillMd = join11(skillsRoot, entry.name, "SKILL.md");
39459
39543
  if (!existsSync3(skillMd)) continue;
39460
39544
  let description = "";
39461
39545
  try {
@@ -39467,7 +39551,7 @@ var init_engine2 = __esm({
39467
39551
  const newEntry = {
39468
39552
  name: entry.name,
39469
39553
  description,
39470
- sourcePath: join10(skillsRoot, entry.name),
39554
+ sourcePath: join11(skillsRoot, entry.name),
39471
39555
  sourceAgent: agent
39472
39556
  };
39473
39557
  const existing = seen.get(entry.name);
@@ -39504,7 +39588,7 @@ var init_engine2 = __esm({
39504
39588
  }
39505
39589
  for (const skill of skills) {
39506
39590
  if (skill.sourceAgent === target) continue;
39507
- const dest = join10(targetDir, skill.name);
39591
+ const dest = join11(targetDir, skill.name);
39508
39592
  if (existsSync3(dest)) {
39509
39593
  skipped.push(`${skill.name} (already exists in ${target})`);
39510
39594
  continue;
@@ -39520,13 +39604,13 @@ var init_engine2 = __esm({
39520
39604
  }
39521
39605
  scanWorkflows() {
39522
39606
  const workflows = [];
39523
- const wfDir = join10(this.projectRoot, ".windsurf", "workflows");
39607
+ const wfDir = join11(this.projectRoot, ".windsurf", "workflows");
39524
39608
  if (!existsSync3(wfDir)) return workflows;
39525
39609
  try {
39526
39610
  const files = readdirSync(wfDir).filter((f4) => f4.endsWith(".md"));
39527
39611
  for (const file of files) {
39528
39612
  try {
39529
- const content = readFileSync2(join10(wfDir, file), "utf-8");
39613
+ const content = readFileSync2(join11(wfDir, file), "utf-8");
39530
39614
  workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
39531
39615
  } catch {
39532
39616
  }
@@ -39615,7 +39699,8 @@ var init_engine2 = __esm({
39615
39699
  windsurf: "windsurf",
39616
39700
  copilot: "copilot",
39617
39701
  antigravity: "antigravity",
39618
- kiro: "kiro"
39702
+ kiro: "kiro",
39703
+ opencode: "codex"
39619
39704
  };
39620
39705
  return map[target] ?? null;
39621
39706
  }
@@ -39917,8 +40002,8 @@ __export(engine_exports, {
39917
40002
  SkillsEngine: () => SkillsEngine
39918
40003
  });
39919
40004
  import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
39920
- import { join as join11 } from "path";
39921
- import { homedir as homedir10 } from "os";
40005
+ import { join as join12 } from "path";
40006
+ import { homedir as homedir11 } from "os";
39922
40007
  var SKILLS_DIRS, SKILL_WORTHY_TYPES, MIN_OBS_FOR_SKILL, MIN_SCORE_FOR_SKILL, SkillsEngine;
39923
40008
  var init_engine3 = __esm({
39924
40009
  "src/skills/engine.ts"() {
@@ -39931,7 +40016,8 @@ var init_engine3 = __esm({
39931
40016
  "claude-code": [".claude/skills"],
39932
40017
  copilot: [".github/skills", ".copilot/skills"],
39933
40018
  antigravity: [".agent/skills", ".gemini/skills", ".gemini/antigravity/skills"],
39934
- kiro: [".kiro/skills"]
40019
+ kiro: [".kiro/skills"],
40020
+ opencode: [".opencode/skills"]
39935
40021
  };
39936
40022
  SKILL_WORTHY_TYPES = /* @__PURE__ */ new Set([
39937
40023
  "gotcha",
@@ -39957,12 +40043,12 @@ var init_engine3 = __esm({
39957
40043
  listSkills() {
39958
40044
  const skills = [];
39959
40045
  const seen = /* @__PURE__ */ new Set();
39960
- const home = homedir10();
40046
+ const home = homedir11();
39961
40047
  for (const [agent, dirs] of Object.entries(SKILLS_DIRS)) {
39962
40048
  for (const dir of dirs) {
39963
- const paths = [join11(this.projectRoot, dir)];
40049
+ const paths = [join12(this.projectRoot, dir)];
39964
40050
  if (!this.skipGlobal) {
39965
- paths.push(join11(home, dir));
40051
+ paths.push(join12(home, dir));
39966
40052
  }
39967
40053
  for (const skillsRoot of paths) {
39968
40054
  if (!existsSync4(skillsRoot)) continue;
@@ -39972,7 +40058,7 @@ var init_engine3 = __esm({
39972
40058
  if (!entry.isDirectory()) continue;
39973
40059
  const name = entry.name;
39974
40060
  if (seen.has(name)) continue;
39975
- const skillMd = join11(skillsRoot, name, "SKILL.md");
40061
+ const skillMd = join12(skillsRoot, name, "SKILL.md");
39976
40062
  if (!existsSync4(skillMd)) continue;
39977
40063
  try {
39978
40064
  const content = readFileSync3(skillMd, "utf-8");
@@ -39980,7 +40066,7 @@ var init_engine3 = __esm({
39980
40066
  skills.push({
39981
40067
  name,
39982
40068
  description,
39983
- sourcePath: join11(skillsRoot, name),
40069
+ sourcePath: join12(skillsRoot, name),
39984
40070
  sourceAgent: agent,
39985
40071
  content,
39986
40072
  generated: false
@@ -40022,11 +40108,11 @@ var init_engine3 = __esm({
40022
40108
  writeSkill(skill, target) {
40023
40109
  const dirs = SKILLS_DIRS[target];
40024
40110
  if (!dirs || dirs.length === 0) return null;
40025
- const targetDir = join11(this.projectRoot, dirs[0], skill.name);
40111
+ const targetDir = join12(this.projectRoot, dirs[0], skill.name);
40026
40112
  try {
40027
40113
  mkdirSync3(targetDir, { recursive: true });
40028
- writeFileSync2(join11(targetDir, "SKILL.md"), skill.content, "utf-8");
40029
- return join11(dirs[0], skill.name, "SKILL.md");
40114
+ writeFileSync2(join12(targetDir, "SKILL.md"), skill.content, "utf-8");
40115
+ return join12(dirs[0], skill.name, "SKILL.md");
40030
40116
  } catch {
40031
40117
  return null;
40032
40118
  }
@@ -41011,6 +41097,91 @@ function generateKiroHookFiles() {
41011
41097
  }
41012
41098
  ];
41013
41099
  }
41100
+ function generateOpenCodePlugin() {
41101
+ return `/**
41102
+ * Memorix \u2014 Cross-Agent Memory Bridge Plugin for OpenCode
41103
+ *
41104
+ * Automatically captures session context and tool usage,
41105
+ * piping events to \`memorix hook\` for cross-agent memory persistence.
41106
+ *
41107
+ * Generated by: memorix installHooks('opencode', projectRoot)
41108
+ * Docs: https://github.com/AVIDS2/memorix
41109
+ */
41110
+ export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
41111
+ console.log('[memorix] plugin loaded, directory:', directory);
41112
+
41113
+ /** Pipe event JSON to memorix hook via temp file (Windows .cmd stdin workaround) */
41114
+ async function runHook(payload) {
41115
+ const tmpDir = Bun.env.TEMP || Bun.env.TMP || '/tmp';
41116
+ const tmpPath = \`\${tmpDir}/memorix-hook-\${Date.now()}.json\`;
41117
+ try {
41118
+ const data = JSON.stringify(payload);
41119
+ await Bun.write(tmpPath, data);
41120
+ // cat | pipe works through .cmd wrappers; < redirect does NOT
41121
+ await $\`cat \${tmpPath} | memorix hook\`.quiet().nothrow();
41122
+ console.log('[memorix] hook fired:', payload.hook_event_name);
41123
+ } catch (err) {
41124
+ console.log('[memorix] hook error:', err?.message ?? err);
41125
+ } finally {
41126
+ try { const { unlinkSync } = await import('node:fs'); unlinkSync(tmpPath); } catch {}
41127
+ }
41128
+ }
41129
+
41130
+ return {
41131
+ /** Catch-all event handler for session lifecycle + file events */
41132
+ event: async ({ event }) => {
41133
+ if (event.type === 'session.created') {
41134
+ await runHook({
41135
+ agent: 'opencode',
41136
+ hook_event_name: 'session.created',
41137
+ cwd: directory,
41138
+ });
41139
+ } else if (event.type === 'session.idle') {
41140
+ await runHook({
41141
+ agent: 'opencode',
41142
+ hook_event_name: 'session.idle',
41143
+ cwd: directory,
41144
+ });
41145
+ } else if (event.type === 'file.edited') {
41146
+ await runHook({
41147
+ agent: 'opencode',
41148
+ hook_event_name: 'file.edited',
41149
+ file_path: event.properties?.path ?? '',
41150
+ cwd: directory,
41151
+ });
41152
+ } else if (event.type === 'command.executed') {
41153
+ await runHook({
41154
+ agent: 'opencode',
41155
+ hook_event_name: 'command.executed',
41156
+ command: event.properties?.command ?? '',
41157
+ cwd: directory,
41158
+ });
41159
+ }
41160
+ },
41161
+
41162
+ /** Record tool usage after execution (hook, not event) */
41163
+ 'tool.execute.after': async (input, output) => {
41164
+ await runHook({
41165
+ agent: 'opencode',
41166
+ hook_event_name: 'tool.execute.after',
41167
+ tool_name: input.tool,
41168
+ tool_input: input.args,
41169
+ cwd: directory,
41170
+ });
41171
+ },
41172
+
41173
+ /** Inject memorix context into compaction prompt */
41174
+ 'experimental.session.compacting': async (input, output) => {
41175
+ output.context.push(
41176
+ '## Memorix Cross-Agent Memory\\n' +
41177
+ 'Before compacting, use memorix_store to save important discoveries, decisions, and gotchas.\\n' +
41178
+ 'After compacting, use memorix_search to reload relevant context.'
41179
+ );
41180
+ },
41181
+ };
41182
+ };
41183
+ `;
41184
+ }
41014
41185
  function getProjectConfigPath(agent, projectRoot) {
41015
41186
  switch (agent) {
41016
41187
  case "claude":
@@ -41025,6 +41196,8 @@ function getProjectConfigPath(agent, projectRoot) {
41025
41196
  return path8.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
41026
41197
  case "codex":
41027
41198
  return path8.join(projectRoot, "AGENTS.md");
41199
+ case "opencode":
41200
+ return path8.join(projectRoot, ".opencode", "plugins", "memorix.js");
41028
41201
  case "antigravity":
41029
41202
  return path8.join(projectRoot, ".gemini", "settings.json");
41030
41203
  default:
@@ -41043,6 +41216,8 @@ function getGlobalConfigPath(agent) {
41043
41216
  return path8.join(home, ".cursor", "hooks.json");
41044
41217
  case "antigravity":
41045
41218
  return path8.join(home, ".gemini", "settings.json");
41219
+ case "opencode":
41220
+ return path8.join(home, ".config", "opencode", "plugins", "memorix.js");
41046
41221
  default:
41047
41222
  return path8.join(home, ".memorix", "hooks.json");
41048
41223
  }
@@ -41092,6 +41267,12 @@ async function detectInstalledAgents() {
41092
41267
  agents.push("antigravity");
41093
41268
  } catch {
41094
41269
  }
41270
+ const opencodeDir = path8.join(home, ".config", "opencode");
41271
+ try {
41272
+ await fs6.access(opencodeDir);
41273
+ agents.push("opencode");
41274
+ } catch {
41275
+ }
41095
41276
  return agents;
41096
41277
  }
41097
41278
  async function installHooks(agent, projectRoot, global = false) {
@@ -41124,6 +41305,19 @@ async function installHooks(agent, projectRoot, global = false) {
41124
41305
  events: [],
41125
41306
  generated: { note: "Codex has no hooks system, only rules (AGENTS.md) installed" }
41126
41307
  };
41308
+ case "opencode": {
41309
+ const pluginContent = generateOpenCodePlugin();
41310
+ const pluginPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
41311
+ await fs6.mkdir(path8.dirname(pluginPath), { recursive: true });
41312
+ await fs6.writeFile(pluginPath, pluginContent, "utf-8");
41313
+ await installAgentRules(agent, projectRoot);
41314
+ return {
41315
+ agent,
41316
+ configPath: pluginPath,
41317
+ events: ["session_start", "session_end", "post_tool", "post_edit", "pre_compact"],
41318
+ generated: { note: "OpenCode plugin installed at " + pluginPath }
41319
+ };
41320
+ }
41127
41321
  default:
41128
41322
  generated = generateClaudeConfig();
41129
41323
  }
@@ -41216,6 +41410,9 @@ async function installAgentRules(agent, projectRoot) {
41216
41410
  case "kiro":
41217
41411
  rulesPath = path8.join(projectRoot, ".kiro", "steering", "memorix.md");
41218
41412
  break;
41413
+ case "opencode":
41414
+ rulesPath = path8.join(projectRoot, "AGENTS.md");
41415
+ break;
41219
41416
  case "antigravity":
41220
41417
  rulesPath = path8.join(projectRoot, "GEMINI.md");
41221
41418
  break;
@@ -41225,7 +41422,7 @@ async function installAgentRules(agent, projectRoot) {
41225
41422
  }
41226
41423
  try {
41227
41424
  await fs6.mkdir(path8.dirname(rulesPath), { recursive: true });
41228
- if (agent === "codex" || agent === "antigravity") {
41425
+ if (agent === "codex" || agent === "opencode" || agent === "antigravity") {
41229
41426
  try {
41230
41427
  const existing = await fs6.readFile(rulesPath, "utf-8");
41231
41428
  if (existing.includes("Memorix")) {
@@ -41342,7 +41539,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
41342
41539
  }
41343
41540
  async function getHookStatus(projectRoot) {
41344
41541
  const results = [];
41345
- const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex", "antigravity"];
41542
+ const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex", "antigravity", "opencode"];
41346
41543
  for (const agent of agents) {
41347
41544
  const projectPath = getProjectConfigPath(agent, projectRoot);
41348
41545
  const globalPath = getGlobalConfigPath(agent);
@@ -42544,10 +42741,10 @@ ${json}
42544
42741
  try {
42545
42742
  let autoInstall = true;
42546
42743
  try {
42547
- const { homedir: homedir12 } = await import("os");
42548
- const { join: join13 } = await import("path");
42744
+ const { homedir: homedir13 } = await import("os");
42745
+ const { join: join14 } = await import("path");
42549
42746
  const { readFile: readFile3 } = await import("fs/promises");
42550
- const settingsPath = join13(homedir12(), ".memorix", "settings.json");
42747
+ const settingsPath = join14(homedir13(), ".memorix", "settings.json");
42551
42748
  const raw = await readFile3(settingsPath, "utf-8");
42552
42749
  const settings = JSON.parse(raw);
42553
42750
  if (settings.autoInstallHooks === false) {
@@ -42617,11 +42814,15 @@ ${json}
42617
42814
  }
42618
42815
  const observationsFile = projectDir2 + "/observations.json";
42619
42816
  let reloadDebounce = null;
42817
+ let reloading = false;
42620
42818
  try {
42621
- watchFile(observationsFile, { interval: 2e3 }, (curr, prev) => {
42819
+ watchFile(observationsFile, { interval: 5e3 }, (curr, prev) => {
42622
42820
  if (curr.mtimeMs === prev.mtimeMs) return;
42821
+ if (reloading) return;
42623
42822
  if (reloadDebounce) clearTimeout(reloadDebounce);
42624
42823
  reloadDebounce = setTimeout(async () => {
42824
+ if (reloading) return;
42825
+ reloading = true;
42625
42826
  try {
42626
42827
  await resetDb();
42627
42828
  await initObservations(projectDir2);
@@ -42631,7 +42832,8 @@ ${json}
42631
42832
  }
42632
42833
  } catch {
42633
42834
  }
42634
- }, 500);
42835
+ reloading = false;
42836
+ }, 3e3);
42635
42837
  });
42636
42838
  console.error(`[memorix] Watching for external writes (hooks hot-reload enabled)`);
42637
42839
  } catch {
@@ -42701,7 +42903,7 @@ var init_serve = __esm({
42701
42903
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
42702
42904
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
42703
42905
  const { existsSync: existsSync5 } = await import("fs");
42704
- const { join: join13 } = await import("path");
42906
+ const { join: join14 } = await import("path");
42705
42907
  process.stdin.on("end", () => {
42706
42908
  console.error("[memorix] stdin closed \u2014 exiting");
42707
42909
  process.exit(0);
@@ -42714,7 +42916,7 @@ var init_serve = __esm({
42714
42916
  }
42715
42917
  let projectRoot = args.cwd || process.env.MEMORIX_PROJECT_ROOT || process.env.INIT_CWD || safeCwd;
42716
42918
  console.error(`[memorix] Starting with cwd: ${projectRoot}`);
42717
- const looksValid = existsSync5(join13(projectRoot, ".git")) || existsSync5(join13(projectRoot, "package.json")) || existsSync5(join13(projectRoot, "Cargo.toml")) || existsSync5(join13(projectRoot, "go.mod")) || existsSync5(join13(projectRoot, "pyproject.toml"));
42919
+ const looksValid = existsSync5(join14(projectRoot, ".git")) || existsSync5(join14(projectRoot, "package.json")) || existsSync5(join14(projectRoot, "Cargo.toml")) || existsSync5(join14(projectRoot, "go.mod")) || existsSync5(join14(projectRoot, "pyproject.toml"));
42718
42920
  if (!looksValid) {
42719
42921
  const earlyDetect = detectProject2(projectRoot);
42720
42922
  const isDegraded = earlyDetect.id.startsWith("placeholder/");
@@ -43511,13 +43713,13 @@ var init_status = __esm({
43511
43713
  const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
43512
43714
  const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
43513
43715
  const { existsSync: existsSync5, readFileSync: readFileSync4 } = await import("fs");
43514
- const { join: join13 } = await import("path");
43716
+ const { join: join14 } = await import("path");
43515
43717
  we("memorix status");
43516
43718
  const project = detectProject2();
43517
43719
  const dataDir = await getProjectDataDir2(project.id);
43518
43720
  let obsCount = 0;
43519
43721
  try {
43520
- const obsFile = join13(dataDir, "observations.json");
43722
+ const obsFile = join14(dataDir, "observations.json");
43521
43723
  if (existsSync5(obsFile)) {
43522
43724
  const data = JSON.parse(readFileSync4(obsFile, "utf-8"));
43523
43725
  obsCount = Array.isArray(data) ? data.length : 0;
@@ -43704,6 +43906,7 @@ function detectAgent(payload) {
43704
43906
  if ("agent_action_name" in payload) return "windsurf";
43705
43907
  if ("conversation_id" in payload || "cursor_version" in payload) return "cursor";
43706
43908
  if ("gemini_session_id" in payload || "gemini_project_dir" in payload) return "antigravity";
43909
+ if (payload.agent === "opencode") return "opencode";
43707
43910
  if ("hook_event_name" in payload && "session_id" in payload) return "claude";
43708
43911
  if ("hook_event_name" in payload) {
43709
43912
  return "claude";
@@ -43724,6 +43927,8 @@ function extractEventName(payload, agent) {
43724
43927
  return payload.hook_event_name ?? "";
43725
43928
  case "copilot":
43726
43929
  return inferCopilotEvent(payload);
43930
+ case "opencode":
43931
+ return payload.hook_event_name ?? "";
43727
43932
  case "kiro":
43728
43933
  return payload.event_type ?? "";
43729
43934
  default:
@@ -43894,6 +44099,34 @@ function normalizeGemini(payload, event) {
43894
44099
  }
43895
44100
  return result;
43896
44101
  }
44102
+ function normalizeOpenCode(payload, event) {
44103
+ const result = {
44104
+ sessionId: "",
44105
+ cwd: payload.cwd ?? ""
44106
+ };
44107
+ const toolName = payload.tool_name ?? "";
44108
+ if (toolName) {
44109
+ result.toolName = toolName;
44110
+ result.toolInput = payload.tool_input;
44111
+ const toolResult = payload.tool_result;
44112
+ if (typeof toolResult === "string") {
44113
+ result.toolResult = toolResult;
44114
+ } else if (toolResult && typeof toolResult === "object") {
44115
+ result.toolResult = JSON.stringify(toolResult);
44116
+ }
44117
+ }
44118
+ if (event === "post_edit") {
44119
+ result.filePath = payload.file_path ?? "";
44120
+ }
44121
+ if (event === "post_tool" && result.toolInput) {
44122
+ const fp = result.toolInput.path ?? result.toolInput.file_path;
44123
+ if (fp) result.filePath = fp;
44124
+ }
44125
+ if (event === "post_command") {
44126
+ result.command = payload.command ?? "";
44127
+ }
44128
+ return result;
44129
+ }
43897
44130
  function normalizeHookInput(payload) {
43898
44131
  const directEvent = typeof payload.event === "string" ? EVENT_MAP[payload.event] : void 0;
43899
44132
  const agent = detectAgent(payload);
@@ -43917,6 +44150,9 @@ function normalizeHookInput(payload) {
43917
44150
  case "antigravity":
43918
44151
  agentSpecific = normalizeGemini(payload, event);
43919
44152
  break;
44153
+ case "opencode":
44154
+ agentSpecific = normalizeOpenCode(payload, event);
44155
+ break;
43920
44156
  default:
43921
44157
  agentSpecific = { sessionId: "", cwd: "" };
43922
44158
  }
@@ -43988,7 +44224,15 @@ var init_normalizer = __esm({
43988
44224
  afterMCPExecution: "post_tool",
43989
44225
  afterFileEdit: "post_edit",
43990
44226
  preCompact: "pre_compact",
43991
- stop: "session_end"
44227
+ stop: "session_end",
44228
+ // OpenCode (plugin events piped via Bun.spawn → memorix hook)
44229
+ "session.created": "session_start",
44230
+ "session.idle": "session_end",
44231
+ "session.compacted": "pre_compact",
44232
+ "tool.execute.after": "post_tool",
44233
+ "file.edited": "post_edit",
44234
+ "command.executed": "post_command",
44235
+ "message.updated": "post_response"
43992
44236
  };
43993
44237
  }
43994
44238
  });