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 +282 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +228 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
39279
|
-
import { homedir as
|
|
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
|
|
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 =
|
|
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
|
-
|
|
39450
|
-
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
39921
|
-
import { homedir as
|
|
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 =
|
|
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 = [
|
|
40049
|
+
const paths = [join12(this.projectRoot, dir)];
|
|
39964
40050
|
if (!this.skipGlobal) {
|
|
39965
|
-
paths.push(
|
|
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 =
|
|
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:
|
|
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 =
|
|
40111
|
+
const targetDir = join12(this.projectRoot, dirs[0], skill.name);
|
|
40026
40112
|
try {
|
|
40027
40113
|
mkdirSync3(targetDir, { recursive: true });
|
|
40028
|
-
writeFileSync2(
|
|
40029
|
-
return
|
|
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:
|
|
42548
|
-
const { join:
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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:
|
|
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 =
|
|
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
|
});
|