memorix 1.9.7 → 1.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/cli/index.js +158 -3
- package/dist/index.js +158 -3
- package/dist/memcode-runtime/CHANGELOG.md +7 -0
- package/dist/sdk.js +157 -2
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/README.md +1 -1
- package/docs/SETUP.md +1 -1
- package/package.json +1 -1
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/hooks/installers/index.ts +160 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
|
|
7
7
|
No unreleased changes.
|
|
8
8
|
|
|
9
|
+
## [1.9.8] - 2026-10-02
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **OpenCode v2 plugin compatibility** -- generated plugins retain the v1
|
|
13
|
+
named export while also exposing the v2 default `id`/`setup` contract,
|
|
14
|
+
including event subscriptions, tool hooks, cleanup, and host session IDs.
|
|
15
|
+
|
|
9
16
|
## [1.9.7] - 2026-09-28
|
|
10
17
|
|
|
11
18
|
### Fixed
|
package/dist/cli/index.js
CHANGED
|
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3232
3232
|
|
|
3233
3233
|
// src/cli/version.ts
|
|
3234
3234
|
function getCliVersion() {
|
|
3235
|
-
return true ? "1.9.
|
|
3235
|
+
return true ? "1.9.8" : pkg.version;
|
|
3236
3236
|
}
|
|
3237
3237
|
var init_version = __esm({
|
|
3238
3238
|
"src/cli/version.ts"() {
|
|
@@ -195667,6 +195667,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
195667
195667
|
},
|
|
195668
195668
|
};
|
|
195669
195669
|
};
|
|
195670
|
+
|
|
195671
|
+
/**
|
|
195672
|
+
* OpenCode v2 compatibility layer. Keep this beside the v1 export so one
|
|
195673
|
+
* generated file can be loaded by both host generations during migration.
|
|
195674
|
+
*/
|
|
195675
|
+
const v2HookCommand = ${hookCommand};
|
|
195676
|
+
|
|
195677
|
+
function v2ReportHookFailure(eventName, detail) {
|
|
195678
|
+
if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
|
|
195679
|
+
console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
|
|
195680
|
+
}
|
|
195681
|
+
|
|
195682
|
+
function v2EventData(event) {
|
|
195683
|
+
return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
|
|
195684
|
+
}
|
|
195685
|
+
|
|
195686
|
+
function v2SessionId(event) {
|
|
195687
|
+
const data = v2EventData(event);
|
|
195688
|
+
const value = data.sessionID ?? data.sessionId ?? data.session_id
|
|
195689
|
+
?? event?.sessionID ?? event?.sessionId;
|
|
195690
|
+
return typeof value === 'string' ? value : '';
|
|
195691
|
+
}
|
|
195692
|
+
|
|
195693
|
+
function v2RunHook(payload, directory, sessionId) {
|
|
195694
|
+
const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
|
|
195695
|
+
if (sessionId) nextPayload.session_id = sessionId;
|
|
195696
|
+
const eventName = nextPayload.hook_event_name || 'unknown';
|
|
195697
|
+
try {
|
|
195698
|
+
const result = spawnSync(v2HookCommand, ['hook'], {
|
|
195699
|
+
input: JSON.stringify(nextPayload),
|
|
195700
|
+
timeout: 10_000,
|
|
195701
|
+
encoding: 'utf-8',
|
|
195702
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
195703
|
+
shell: process.platform === 'win32',
|
|
195704
|
+
windowsHide: true,
|
|
195705
|
+
});
|
|
195706
|
+
if (result.status !== 0) {
|
|
195707
|
+
v2ReportHookFailure(eventName, {
|
|
195708
|
+
exit: result.status,
|
|
195709
|
+
stderr: (result.stderr || '').slice(0, 200),
|
|
195710
|
+
error: result.error?.message,
|
|
195711
|
+
});
|
|
195712
|
+
}
|
|
195713
|
+
} catch (error) {
|
|
195714
|
+
v2ReportHookFailure(eventName, error?.message ?? error);
|
|
195715
|
+
}
|
|
195716
|
+
}
|
|
195717
|
+
|
|
195718
|
+
function v2SessionState(states, sessionId) {
|
|
195719
|
+
const key = sessionId || '__unknown__';
|
|
195720
|
+
let state = states.get(key);
|
|
195721
|
+
if (!state) {
|
|
195722
|
+
state = { pendingText: '', lastDeliveredKey: '' };
|
|
195723
|
+
states.set(key, state);
|
|
195724
|
+
}
|
|
195725
|
+
return state;
|
|
195726
|
+
}
|
|
195727
|
+
|
|
195728
|
+
function v2FlushAssistant(state, directory, sessionId) {
|
|
195729
|
+
const text = state.pendingText.trim();
|
|
195730
|
+
if (!text || text === state.lastDeliveredKey) return;
|
|
195731
|
+
v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
|
|
195732
|
+
state.lastDeliveredKey = text;
|
|
195733
|
+
state.pendingText = '';
|
|
195734
|
+
}
|
|
195735
|
+
|
|
195736
|
+
function v2FilePath(file) {
|
|
195737
|
+
if (typeof file === 'string') return file;
|
|
195738
|
+
if (!file || typeof file !== 'object') return '';
|
|
195739
|
+
return file.path ?? file.file ?? file.name ?? '';
|
|
195740
|
+
}
|
|
195741
|
+
|
|
195742
|
+
function v2HandleEvent(event, directory, states) {
|
|
195743
|
+
if (!event || typeof event !== 'object') return;
|
|
195744
|
+
const type = typeof event.type === 'string' ? event.type : '';
|
|
195745
|
+
const data = v2EventData(event);
|
|
195746
|
+
const sessionId = v2SessionId(event);
|
|
195747
|
+
const state = v2SessionState(states, sessionId);
|
|
195748
|
+
const run = (payload) => v2RunHook(payload, directory, sessionId);
|
|
195749
|
+
|
|
195750
|
+
switch (type) {
|
|
195751
|
+
case 'session.created':
|
|
195752
|
+
run({ hook_event_name: 'session.created' });
|
|
195753
|
+
break;
|
|
195754
|
+
case 'session.text.delta':
|
|
195755
|
+
state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
|
|
195756
|
+
break;
|
|
195757
|
+
case 'session.message.updated':
|
|
195758
|
+
case 'message.updated':
|
|
195759
|
+
if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
|
|
195760
|
+
break;
|
|
195761
|
+
case 'session.step.ended': {
|
|
195762
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
195763
|
+
for (const file of files) {
|
|
195764
|
+
const filePath = v2FilePath(file);
|
|
195765
|
+
if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
|
|
195766
|
+
}
|
|
195767
|
+
if (typeof data.text === 'string') state.pendingText += data.text;
|
|
195768
|
+
break;
|
|
195769
|
+
}
|
|
195770
|
+
case 'session.idle':
|
|
195771
|
+
v2FlushAssistant(state, directory, sessionId);
|
|
195772
|
+
run({ hook_event_name: 'session.idle' });
|
|
195773
|
+
break;
|
|
195774
|
+
case 'session.compacted':
|
|
195775
|
+
run({ hook_event_name: 'session.compacted' });
|
|
195776
|
+
break;
|
|
195777
|
+
default:
|
|
195778
|
+
break;
|
|
195779
|
+
}
|
|
195780
|
+
}
|
|
195781
|
+
|
|
195782
|
+
function setup(ctx) {
|
|
195783
|
+
const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
|
|
195784
|
+
const controller = new AbortController();
|
|
195785
|
+
const states = new Map();
|
|
195786
|
+
|
|
195787
|
+
if (ctx?.tool?.hook) {
|
|
195788
|
+
try {
|
|
195789
|
+
Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
|
|
195790
|
+
const sessionId = v2SessionId(event);
|
|
195791
|
+
const data = event && typeof event.input === 'object' ? event.input : {};
|
|
195792
|
+
const tool = event?.tool ?? event?.name ?? '';
|
|
195793
|
+
v2RunHook({
|
|
195794
|
+
hook_event_name: 'tool.execute.after',
|
|
195795
|
+
tool_name: tool,
|
|
195796
|
+
tool_input: data,
|
|
195797
|
+
}, directory, sessionId);
|
|
195798
|
+
if (tool === 'shell') {
|
|
195799
|
+
v2RunHook({
|
|
195800
|
+
hook_event_name: 'command.executed',
|
|
195801
|
+
command: data.command ?? data.cmd ?? '',
|
|
195802
|
+
}, directory, sessionId);
|
|
195803
|
+
}
|
|
195804
|
+
})).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
|
|
195805
|
+
} catch (error) {
|
|
195806
|
+
v2ReportHookFailure('execute.after', error?.message ?? error);
|
|
195807
|
+
}
|
|
195808
|
+
}
|
|
195809
|
+
|
|
195810
|
+
if (ctx?.event?.subscribe) {
|
|
195811
|
+
void (async () => {
|
|
195812
|
+
try {
|
|
195813
|
+
const stream = ctx.event.subscribe({ signal: controller.signal });
|
|
195814
|
+
for await (const event of stream) v2HandleEvent(event, directory, states);
|
|
195815
|
+
} catch (error) {
|
|
195816
|
+
if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
|
|
195817
|
+
}
|
|
195818
|
+
})();
|
|
195819
|
+
}
|
|
195820
|
+
|
|
195821
|
+
return () => controller.abort();
|
|
195822
|
+
}
|
|
195823
|
+
|
|
195824
|
+
export default { id: 'memorix', setup };
|
|
195670
195825
|
`;
|
|
195671
195826
|
}
|
|
195672
195827
|
async function installOpenCodeSkills(projectRoot, global2 = false) {
|
|
@@ -196492,7 +196647,7 @@ var init_installers = __esm({
|
|
|
196492
196647
|
"use strict";
|
|
196493
196648
|
init_esm_shims();
|
|
196494
196649
|
init_official_skills();
|
|
196495
|
-
OPENCODE_PLUGIN_VERSION =
|
|
196650
|
+
OPENCODE_PLUGIN_VERSION = 8;
|
|
196496
196651
|
AGENT_SKILL_DIRS = {
|
|
196497
196652
|
cursor: { project: path31.join(".cursor", "skills"), global: path31.join(".cursor", "skills") },
|
|
196498
196653
|
windsurf: { project: path31.join(".windsurf", "skills"), global: path31.join(".windsurf", "skills") },
|
|
@@ -209019,7 +209174,7 @@ var init_tool_profile = __esm({
|
|
|
209019
209174
|
function getMcpServerInfo() {
|
|
209020
209175
|
return {
|
|
209021
209176
|
name: "memorix",
|
|
209022
|
-
version: true ? "1.9.
|
|
209177
|
+
version: true ? "1.9.8" : "1.0.1"
|
|
209023
209178
|
};
|
|
209024
209179
|
}
|
|
209025
209180
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
package/dist/index.js
CHANGED
|
@@ -160104,7 +160104,7 @@ var init_request_context = __esm({
|
|
|
160104
160104
|
function getMcpServerInfo() {
|
|
160105
160105
|
return {
|
|
160106
160106
|
name: "memorix",
|
|
160107
|
-
version: true ? "1.9.
|
|
160107
|
+
version: true ? "1.9.8" : "1.0.1"
|
|
160108
160108
|
};
|
|
160109
160109
|
}
|
|
160110
160110
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -170093,6 +170093,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
170093
170093
|
},
|
|
170094
170094
|
};
|
|
170095
170095
|
};
|
|
170096
|
+
|
|
170097
|
+
/**
|
|
170098
|
+
* OpenCode v2 compatibility layer. Keep this beside the v1 export so one
|
|
170099
|
+
* generated file can be loaded by both host generations during migration.
|
|
170100
|
+
*/
|
|
170101
|
+
const v2HookCommand = ${hookCommand};
|
|
170102
|
+
|
|
170103
|
+
function v2ReportHookFailure(eventName, detail) {
|
|
170104
|
+
if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
|
|
170105
|
+
console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
|
|
170106
|
+
}
|
|
170107
|
+
|
|
170108
|
+
function v2EventData(event) {
|
|
170109
|
+
return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
|
|
170110
|
+
}
|
|
170111
|
+
|
|
170112
|
+
function v2SessionId(event) {
|
|
170113
|
+
const data = v2EventData(event);
|
|
170114
|
+
const value = data.sessionID ?? data.sessionId ?? data.session_id
|
|
170115
|
+
?? event?.sessionID ?? event?.sessionId;
|
|
170116
|
+
return typeof value === 'string' ? value : '';
|
|
170117
|
+
}
|
|
170118
|
+
|
|
170119
|
+
function v2RunHook(payload, directory, sessionId) {
|
|
170120
|
+
const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
|
|
170121
|
+
if (sessionId) nextPayload.session_id = sessionId;
|
|
170122
|
+
const eventName = nextPayload.hook_event_name || 'unknown';
|
|
170123
|
+
try {
|
|
170124
|
+
const result = spawnSync(v2HookCommand, ['hook'], {
|
|
170125
|
+
input: JSON.stringify(nextPayload),
|
|
170126
|
+
timeout: 10_000,
|
|
170127
|
+
encoding: 'utf-8',
|
|
170128
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
170129
|
+
shell: process.platform === 'win32',
|
|
170130
|
+
windowsHide: true,
|
|
170131
|
+
});
|
|
170132
|
+
if (result.status !== 0) {
|
|
170133
|
+
v2ReportHookFailure(eventName, {
|
|
170134
|
+
exit: result.status,
|
|
170135
|
+
stderr: (result.stderr || '').slice(0, 200),
|
|
170136
|
+
error: result.error?.message,
|
|
170137
|
+
});
|
|
170138
|
+
}
|
|
170139
|
+
} catch (error) {
|
|
170140
|
+
v2ReportHookFailure(eventName, error?.message ?? error);
|
|
170141
|
+
}
|
|
170142
|
+
}
|
|
170143
|
+
|
|
170144
|
+
function v2SessionState(states, sessionId) {
|
|
170145
|
+
const key = sessionId || '__unknown__';
|
|
170146
|
+
let state = states.get(key);
|
|
170147
|
+
if (!state) {
|
|
170148
|
+
state = { pendingText: '', lastDeliveredKey: '' };
|
|
170149
|
+
states.set(key, state);
|
|
170150
|
+
}
|
|
170151
|
+
return state;
|
|
170152
|
+
}
|
|
170153
|
+
|
|
170154
|
+
function v2FlushAssistant(state, directory, sessionId) {
|
|
170155
|
+
const text = state.pendingText.trim();
|
|
170156
|
+
if (!text || text === state.lastDeliveredKey) return;
|
|
170157
|
+
v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
|
|
170158
|
+
state.lastDeliveredKey = text;
|
|
170159
|
+
state.pendingText = '';
|
|
170160
|
+
}
|
|
170161
|
+
|
|
170162
|
+
function v2FilePath(file) {
|
|
170163
|
+
if (typeof file === 'string') return file;
|
|
170164
|
+
if (!file || typeof file !== 'object') return '';
|
|
170165
|
+
return file.path ?? file.file ?? file.name ?? '';
|
|
170166
|
+
}
|
|
170167
|
+
|
|
170168
|
+
function v2HandleEvent(event, directory, states) {
|
|
170169
|
+
if (!event || typeof event !== 'object') return;
|
|
170170
|
+
const type = typeof event.type === 'string' ? event.type : '';
|
|
170171
|
+
const data = v2EventData(event);
|
|
170172
|
+
const sessionId = v2SessionId(event);
|
|
170173
|
+
const state = v2SessionState(states, sessionId);
|
|
170174
|
+
const run = (payload) => v2RunHook(payload, directory, sessionId);
|
|
170175
|
+
|
|
170176
|
+
switch (type) {
|
|
170177
|
+
case 'session.created':
|
|
170178
|
+
run({ hook_event_name: 'session.created' });
|
|
170179
|
+
break;
|
|
170180
|
+
case 'session.text.delta':
|
|
170181
|
+
state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
|
|
170182
|
+
break;
|
|
170183
|
+
case 'session.message.updated':
|
|
170184
|
+
case 'message.updated':
|
|
170185
|
+
if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
|
|
170186
|
+
break;
|
|
170187
|
+
case 'session.step.ended': {
|
|
170188
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
170189
|
+
for (const file of files) {
|
|
170190
|
+
const filePath = v2FilePath(file);
|
|
170191
|
+
if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
|
|
170192
|
+
}
|
|
170193
|
+
if (typeof data.text === 'string') state.pendingText += data.text;
|
|
170194
|
+
break;
|
|
170195
|
+
}
|
|
170196
|
+
case 'session.idle':
|
|
170197
|
+
v2FlushAssistant(state, directory, sessionId);
|
|
170198
|
+
run({ hook_event_name: 'session.idle' });
|
|
170199
|
+
break;
|
|
170200
|
+
case 'session.compacted':
|
|
170201
|
+
run({ hook_event_name: 'session.compacted' });
|
|
170202
|
+
break;
|
|
170203
|
+
default:
|
|
170204
|
+
break;
|
|
170205
|
+
}
|
|
170206
|
+
}
|
|
170207
|
+
|
|
170208
|
+
function setup(ctx) {
|
|
170209
|
+
const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
|
|
170210
|
+
const controller = new AbortController();
|
|
170211
|
+
const states = new Map();
|
|
170212
|
+
|
|
170213
|
+
if (ctx?.tool?.hook) {
|
|
170214
|
+
try {
|
|
170215
|
+
Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
|
|
170216
|
+
const sessionId = v2SessionId(event);
|
|
170217
|
+
const data = event && typeof event.input === 'object' ? event.input : {};
|
|
170218
|
+
const tool = event?.tool ?? event?.name ?? '';
|
|
170219
|
+
v2RunHook({
|
|
170220
|
+
hook_event_name: 'tool.execute.after',
|
|
170221
|
+
tool_name: tool,
|
|
170222
|
+
tool_input: data,
|
|
170223
|
+
}, directory, sessionId);
|
|
170224
|
+
if (tool === 'shell') {
|
|
170225
|
+
v2RunHook({
|
|
170226
|
+
hook_event_name: 'command.executed',
|
|
170227
|
+
command: data.command ?? data.cmd ?? '',
|
|
170228
|
+
}, directory, sessionId);
|
|
170229
|
+
}
|
|
170230
|
+
})).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
|
|
170231
|
+
} catch (error) {
|
|
170232
|
+
v2ReportHookFailure('execute.after', error?.message ?? error);
|
|
170233
|
+
}
|
|
170234
|
+
}
|
|
170235
|
+
|
|
170236
|
+
if (ctx?.event?.subscribe) {
|
|
170237
|
+
void (async () => {
|
|
170238
|
+
try {
|
|
170239
|
+
const stream = ctx.event.subscribe({ signal: controller.signal });
|
|
170240
|
+
for await (const event of stream) v2HandleEvent(event, directory, states);
|
|
170241
|
+
} catch (error) {
|
|
170242
|
+
if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
|
|
170243
|
+
}
|
|
170244
|
+
})();
|
|
170245
|
+
}
|
|
170246
|
+
|
|
170247
|
+
return () => controller.abort();
|
|
170248
|
+
}
|
|
170249
|
+
|
|
170250
|
+
export default { id: 'memorix', setup };
|
|
170096
170251
|
`;
|
|
170097
170252
|
}
|
|
170098
170253
|
async function installOpenCodeSkills(projectRoot, global2 = false) {
|
|
@@ -170918,7 +171073,7 @@ var init_installers = __esm({
|
|
|
170918
171073
|
"use strict";
|
|
170919
171074
|
init_esm_shims();
|
|
170920
171075
|
init_official_skills();
|
|
170921
|
-
OPENCODE_PLUGIN_VERSION =
|
|
171076
|
+
OPENCODE_PLUGIN_VERSION = 8;
|
|
170922
171077
|
AGENT_SKILL_DIRS = {
|
|
170923
171078
|
cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
|
|
170924
171079
|
windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
|
|
@@ -176442,7 +176597,7 @@ var init_serve_shared = __esm({
|
|
|
176442
176597
|
|
|
176443
176598
|
// src/cli/version.ts
|
|
176444
176599
|
function getCliVersion() {
|
|
176445
|
-
return true ? "1.9.
|
|
176600
|
+
return true ? "1.9.8" : pkg.version;
|
|
176446
176601
|
}
|
|
176447
176602
|
var init_version3 = __esm({
|
|
176448
176603
|
"src/cli/version.ts"() {
|
|
@@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
|
|
7
7
|
No unreleased changes.
|
|
8
8
|
|
|
9
|
+
## [1.9.8] - 2026-10-02
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **OpenCode v2 plugin compatibility** -- generated plugins retain the v1
|
|
13
|
+
named export while also exposing the v2 default `id`/`setup` contract,
|
|
14
|
+
including event subscriptions, tool hooks, cleanup, and host session IDs.
|
|
15
|
+
|
|
9
16
|
## [1.9.7] - 2026-09-28
|
|
10
17
|
|
|
11
18
|
### Fixed
|
package/dist/sdk.js
CHANGED
|
@@ -166086,6 +166086,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
166086
166086
|
},
|
|
166087
166087
|
};
|
|
166088
166088
|
};
|
|
166089
|
+
|
|
166090
|
+
/**
|
|
166091
|
+
* OpenCode v2 compatibility layer. Keep this beside the v1 export so one
|
|
166092
|
+
* generated file can be loaded by both host generations during migration.
|
|
166093
|
+
*/
|
|
166094
|
+
const v2HookCommand = ${hookCommand};
|
|
166095
|
+
|
|
166096
|
+
function v2ReportHookFailure(eventName, detail) {
|
|
166097
|
+
if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
|
|
166098
|
+
console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
|
|
166099
|
+
}
|
|
166100
|
+
|
|
166101
|
+
function v2EventData(event) {
|
|
166102
|
+
return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
|
|
166103
|
+
}
|
|
166104
|
+
|
|
166105
|
+
function v2SessionId(event) {
|
|
166106
|
+
const data = v2EventData(event);
|
|
166107
|
+
const value = data.sessionID ?? data.sessionId ?? data.session_id
|
|
166108
|
+
?? event?.sessionID ?? event?.sessionId;
|
|
166109
|
+
return typeof value === 'string' ? value : '';
|
|
166110
|
+
}
|
|
166111
|
+
|
|
166112
|
+
function v2RunHook(payload, directory, sessionId) {
|
|
166113
|
+
const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
|
|
166114
|
+
if (sessionId) nextPayload.session_id = sessionId;
|
|
166115
|
+
const eventName = nextPayload.hook_event_name || 'unknown';
|
|
166116
|
+
try {
|
|
166117
|
+
const result = spawnSync(v2HookCommand, ['hook'], {
|
|
166118
|
+
input: JSON.stringify(nextPayload),
|
|
166119
|
+
timeout: 10_000,
|
|
166120
|
+
encoding: 'utf-8',
|
|
166121
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
166122
|
+
shell: process.platform === 'win32',
|
|
166123
|
+
windowsHide: true,
|
|
166124
|
+
});
|
|
166125
|
+
if (result.status !== 0) {
|
|
166126
|
+
v2ReportHookFailure(eventName, {
|
|
166127
|
+
exit: result.status,
|
|
166128
|
+
stderr: (result.stderr || '').slice(0, 200),
|
|
166129
|
+
error: result.error?.message,
|
|
166130
|
+
});
|
|
166131
|
+
}
|
|
166132
|
+
} catch (error) {
|
|
166133
|
+
v2ReportHookFailure(eventName, error?.message ?? error);
|
|
166134
|
+
}
|
|
166135
|
+
}
|
|
166136
|
+
|
|
166137
|
+
function v2SessionState(states, sessionId) {
|
|
166138
|
+
const key = sessionId || '__unknown__';
|
|
166139
|
+
let state = states.get(key);
|
|
166140
|
+
if (!state) {
|
|
166141
|
+
state = { pendingText: '', lastDeliveredKey: '' };
|
|
166142
|
+
states.set(key, state);
|
|
166143
|
+
}
|
|
166144
|
+
return state;
|
|
166145
|
+
}
|
|
166146
|
+
|
|
166147
|
+
function v2FlushAssistant(state, directory, sessionId) {
|
|
166148
|
+
const text = state.pendingText.trim();
|
|
166149
|
+
if (!text || text === state.lastDeliveredKey) return;
|
|
166150
|
+
v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
|
|
166151
|
+
state.lastDeliveredKey = text;
|
|
166152
|
+
state.pendingText = '';
|
|
166153
|
+
}
|
|
166154
|
+
|
|
166155
|
+
function v2FilePath(file) {
|
|
166156
|
+
if (typeof file === 'string') return file;
|
|
166157
|
+
if (!file || typeof file !== 'object') return '';
|
|
166158
|
+
return file.path ?? file.file ?? file.name ?? '';
|
|
166159
|
+
}
|
|
166160
|
+
|
|
166161
|
+
function v2HandleEvent(event, directory, states) {
|
|
166162
|
+
if (!event || typeof event !== 'object') return;
|
|
166163
|
+
const type = typeof event.type === 'string' ? event.type : '';
|
|
166164
|
+
const data = v2EventData(event);
|
|
166165
|
+
const sessionId = v2SessionId(event);
|
|
166166
|
+
const state = v2SessionState(states, sessionId);
|
|
166167
|
+
const run = (payload) => v2RunHook(payload, directory, sessionId);
|
|
166168
|
+
|
|
166169
|
+
switch (type) {
|
|
166170
|
+
case 'session.created':
|
|
166171
|
+
run({ hook_event_name: 'session.created' });
|
|
166172
|
+
break;
|
|
166173
|
+
case 'session.text.delta':
|
|
166174
|
+
state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
|
|
166175
|
+
break;
|
|
166176
|
+
case 'session.message.updated':
|
|
166177
|
+
case 'message.updated':
|
|
166178
|
+
if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
|
|
166179
|
+
break;
|
|
166180
|
+
case 'session.step.ended': {
|
|
166181
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
166182
|
+
for (const file of files) {
|
|
166183
|
+
const filePath = v2FilePath(file);
|
|
166184
|
+
if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
|
|
166185
|
+
}
|
|
166186
|
+
if (typeof data.text === 'string') state.pendingText += data.text;
|
|
166187
|
+
break;
|
|
166188
|
+
}
|
|
166189
|
+
case 'session.idle':
|
|
166190
|
+
v2FlushAssistant(state, directory, sessionId);
|
|
166191
|
+
run({ hook_event_name: 'session.idle' });
|
|
166192
|
+
break;
|
|
166193
|
+
case 'session.compacted':
|
|
166194
|
+
run({ hook_event_name: 'session.compacted' });
|
|
166195
|
+
break;
|
|
166196
|
+
default:
|
|
166197
|
+
break;
|
|
166198
|
+
}
|
|
166199
|
+
}
|
|
166200
|
+
|
|
166201
|
+
function setup(ctx) {
|
|
166202
|
+
const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
|
|
166203
|
+
const controller = new AbortController();
|
|
166204
|
+
const states = new Map();
|
|
166205
|
+
|
|
166206
|
+
if (ctx?.tool?.hook) {
|
|
166207
|
+
try {
|
|
166208
|
+
Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
|
|
166209
|
+
const sessionId = v2SessionId(event);
|
|
166210
|
+
const data = event && typeof event.input === 'object' ? event.input : {};
|
|
166211
|
+
const tool = event?.tool ?? event?.name ?? '';
|
|
166212
|
+
v2RunHook({
|
|
166213
|
+
hook_event_name: 'tool.execute.after',
|
|
166214
|
+
tool_name: tool,
|
|
166215
|
+
tool_input: data,
|
|
166216
|
+
}, directory, sessionId);
|
|
166217
|
+
if (tool === 'shell') {
|
|
166218
|
+
v2RunHook({
|
|
166219
|
+
hook_event_name: 'command.executed',
|
|
166220
|
+
command: data.command ?? data.cmd ?? '',
|
|
166221
|
+
}, directory, sessionId);
|
|
166222
|
+
}
|
|
166223
|
+
})).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
|
|
166224
|
+
} catch (error) {
|
|
166225
|
+
v2ReportHookFailure('execute.after', error?.message ?? error);
|
|
166226
|
+
}
|
|
166227
|
+
}
|
|
166228
|
+
|
|
166229
|
+
if (ctx?.event?.subscribe) {
|
|
166230
|
+
void (async () => {
|
|
166231
|
+
try {
|
|
166232
|
+
const stream = ctx.event.subscribe({ signal: controller.signal });
|
|
166233
|
+
for await (const event of stream) v2HandleEvent(event, directory, states);
|
|
166234
|
+
} catch (error) {
|
|
166235
|
+
if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
|
|
166236
|
+
}
|
|
166237
|
+
})();
|
|
166238
|
+
}
|
|
166239
|
+
|
|
166240
|
+
return () => controller.abort();
|
|
166241
|
+
}
|
|
166242
|
+
|
|
166243
|
+
export default { id: 'memorix', setup };
|
|
166089
166244
|
`;
|
|
166090
166245
|
}
|
|
166091
166246
|
async function installOpenCodeSkills(projectRoot, global2 = false) {
|
|
@@ -166911,7 +167066,7 @@ var init_installers = __esm({
|
|
|
166911
167066
|
"use strict";
|
|
166912
167067
|
init_esm_shims();
|
|
166913
167068
|
init_official_skills();
|
|
166914
|
-
OPENCODE_PLUGIN_VERSION =
|
|
167069
|
+
OPENCODE_PLUGIN_VERSION = 8;
|
|
166915
167070
|
AGENT_SKILL_DIRS = {
|
|
166916
167071
|
cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
|
|
166917
167072
|
windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
|
|
@@ -170816,7 +170971,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
|
170816
170971
|
function getMcpServerInfo() {
|
|
170817
170972
|
return {
|
|
170818
170973
|
name: "memorix",
|
|
170819
|
-
version: true ? "1.9.
|
|
170974
|
+
version: true ? "1.9.8" : "1.0.1"
|
|
170820
170975
|
};
|
|
170821
170976
|
}
|
|
170822
170977
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -13,7 +13,7 @@ Memorix is a TypeScript project built around:
|
|
|
13
13
|
|
|
14
14
|
## Current Development Baseline
|
|
15
15
|
|
|
16
|
-
The current release line is **1.9.
|
|
16
|
+
The current release line is **1.9.8** and package metadata is kept synchronized
|
|
17
17
|
with the root package and MCP Registry manifest.
|
|
18
18
|
|
|
19
19
|
Contributors should assume the following areas are part of the current release line:
|
package/docs/INTEGRATIONS.md
CHANGED
|
@@ -55,7 +55,7 @@ Generated guidance also has scope:
|
|
|
55
55
|
| Cursor | `memorix setup --agent cursor --global` | Cursor MCP and rules config | Cursor MCP config, `.cursor/rules/memorix.mdc`, skills, and hook guidance | Reload Cursor after setup so it can pick up project config changes. |
|
|
56
56
|
| Grok Build | `memorix setup --agent grok --global` | `~/.grok/hooks/memorix.json` + `~/.grok/AGENTS.md` | Lifecycle hooks on SessionStart, SessionEnd, UserPromptSubmit, PostToolUse, PostToolUseFailure, PreCompact, PostCompact, Stop, and StopFailure, plus Grok project/global guidance | Uses Grok's own JSON hook directory. MCP stays host-owned unless you pass `--mcp http`, which writes `[mcp_servers.memorix]` with `url = "http://localhost:3211/mcp"` in Grok `config.toml`. Global hooks are trusted; project hooks require Grok `/hooks-trust`. Set `GROK_HOME` to use a custom Grok home. The command is `memorix hook --agent grok`. |
|
|
57
57
|
| Gemini CLI | `memorix setup --agent gemini-cli --global` | Gemini CLI extension | Extension under `~/.gemini/extensions/memorix` with MCP, `GEMINI.md` context, hooks, commands, and skills | Antigravity CLI has an official Gemini CLI migration path, but Gemini CLI remains an active standalone Google CLI target. |
|
|
58
|
-
| OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. |
|
|
58
|
+
| OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. | The generated plugin keeps the v1 named export and also exposes the v2 default `id`/`setup` contract. v2 event subscriptions use the host session ID when forwarding hooks. |
|
|
59
59
|
| OpenClaw | `memorix setup --agent openclaw --global` | OpenClaw compatible bundle | `~/.openclaw/extensions/memorix` with `.mcp.json`, skills, and OpenClaw `HOOK.md`/`handler.ts` hook pack | Setup attempts `openclaw plugins install <path> --force` and `openclaw hooks enable memorix` when OpenClaw is available. |
|
|
60
60
|
| Hermes Agent | `memorix setup --agent hermes --global` | Hermes plugin | Hermes home (`%LOCALAPPDATA%\hermes` on native Windows, `~/.hermes` elsewhere, or `HERMES_HOME`), plugin enablement in `config.yaml`, and `mcp_servers.memorix` | The plugin registers hooks, a slash command, a CLI command, and skills through Hermes' plugin context. Existing YAML keys are preserved. |
|
|
61
61
|
| Oh-my-Pi | `memorix setup --agent omp --global` | Oh-my-Pi `omp.extensions` package | `.omp/packages/memorix` or `~/.omp/agent/packages/memorix`, plus `.omp/mcp.json` or `~/.omp/agent/mcp.json` | Setup attempts `omp plugin link <path>`. This target uses Oh-my-Pi's package/extension manifest, not Pi's legacy package manifest. |
|
package/docs/README.md
CHANGED
|
@@ -126,7 +126,7 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
126
126
|
|
|
127
127
|
## Current Product Line
|
|
128
128
|
|
|
129
|
-
The current product line is **1.9.
|
|
129
|
+
The current product line is **1.9.8**. Multi-device store sync is opt-in and
|
|
130
130
|
keeps local SQLite canonical; existing users do not upload anything until they
|
|
131
131
|
configure a relay provider explicitly.
|
|
132
132
|
The authoritative acceptance contract for the current implementation is
|
package/docs/SETUP.md
CHANGED
|
@@ -502,7 +502,7 @@ OpenCode can also use a local Memorix plugin generated by setup:
|
|
|
502
502
|
memorix setup --agent opencode --global
|
|
503
503
|
```
|
|
504
504
|
|
|
505
|
-
This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope.
|
|
505
|
+
This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope. The generated plugin exports both the legacy v1 hook function and the v2 default `id`/`setup` contract, so OpenCode v1 and v2 can load the same file during the migration window.
|
|
506
506
|
|
|
507
507
|
Antigravity setup installs a native plugin at `~/.gemini/config/plugins/memorix` for global setup or `.agents/plugins/memorix` for workspace setup. The plugin includes stdio MCP, official hooks, rules, and skills:
|
|
508
508
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.8",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.9.
|
|
10
|
+
"version": "1.9.8",
|
|
11
11
|
"websiteUrl": "https://mem.rglens.com",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.9.
|
|
25
|
+
"version": "1.9.8",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
|
@@ -449,8 +449,10 @@ function generateKiroHookFiles(): Array<{ filename: string; content: string }> {
|
|
|
449
449
|
* Format: .opencode/plugins/memorix.js — Bun-compatible JS module
|
|
450
450
|
* See: https://opencode.ai/docs/plugins/
|
|
451
451
|
*
|
|
452
|
-
* Plugin
|
|
453
|
-
* -
|
|
452
|
+
* Plugin contracts:
|
|
453
|
+
* - OpenCode v1: named export `MemorixPlugin` returns an event-hook map.
|
|
454
|
+
* - OpenCode v2: default export contains `id` and `setup(ctx)`, with event
|
|
455
|
+
* subscriptions and tool hooks registered through the context.
|
|
454
456
|
* - Return object keys are EVENT NAMES (e.g. "session.created", "file.edited")
|
|
455
457
|
* - Each key maps to an async handler: (input, output) => { ... }
|
|
456
458
|
* - Session/file/command events: handler receives ({ event }) for event-style hooks
|
|
@@ -464,7 +466,7 @@ function generateKiroHookFiles(): Array<{ filename: string; content: string }> {
|
|
|
464
466
|
* protocol used by all agents. spawnSync works in both Node.js and Bun
|
|
465
467
|
* runtimes (OpenCode may fall back to Node.js on Windows).
|
|
466
468
|
*/
|
|
467
|
-
const OPENCODE_PLUGIN_VERSION =
|
|
469
|
+
const OPENCODE_PLUGIN_VERSION = 8;
|
|
468
470
|
|
|
469
471
|
const AGENT_SKILL_DIRS: Partial<Record<AgentName, { project: string; global?: string }>> = {
|
|
470
472
|
cursor: { project: path.join('.cursor', 'skills'), global: path.join('.cursor', 'skills') },
|
|
@@ -716,6 +718,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
716
718
|
},
|
|
717
719
|
};
|
|
718
720
|
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* OpenCode v2 compatibility layer. Keep this beside the v1 export so one
|
|
724
|
+
* generated file can be loaded by both host generations during migration.
|
|
725
|
+
*/
|
|
726
|
+
const v2HookCommand = ${hookCommand};
|
|
727
|
+
|
|
728
|
+
function v2ReportHookFailure(eventName, detail) {
|
|
729
|
+
if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
|
|
730
|
+
console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function v2EventData(event) {
|
|
734
|
+
return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function v2SessionId(event) {
|
|
738
|
+
const data = v2EventData(event);
|
|
739
|
+
const value = data.sessionID ?? data.sessionId ?? data.session_id
|
|
740
|
+
?? event?.sessionID ?? event?.sessionId;
|
|
741
|
+
return typeof value === 'string' ? value : '';
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function v2RunHook(payload, directory, sessionId) {
|
|
745
|
+
const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
|
|
746
|
+
if (sessionId) nextPayload.session_id = sessionId;
|
|
747
|
+
const eventName = nextPayload.hook_event_name || 'unknown';
|
|
748
|
+
try {
|
|
749
|
+
const result = spawnSync(v2HookCommand, ['hook'], {
|
|
750
|
+
input: JSON.stringify(nextPayload),
|
|
751
|
+
timeout: 10_000,
|
|
752
|
+
encoding: 'utf-8',
|
|
753
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
754
|
+
shell: process.platform === 'win32',
|
|
755
|
+
windowsHide: true,
|
|
756
|
+
});
|
|
757
|
+
if (result.status !== 0) {
|
|
758
|
+
v2ReportHookFailure(eventName, {
|
|
759
|
+
exit: result.status,
|
|
760
|
+
stderr: (result.stderr || '').slice(0, 200),
|
|
761
|
+
error: result.error?.message,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
} catch (error) {
|
|
765
|
+
v2ReportHookFailure(eventName, error?.message ?? error);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function v2SessionState(states, sessionId) {
|
|
770
|
+
const key = sessionId || '__unknown__';
|
|
771
|
+
let state = states.get(key);
|
|
772
|
+
if (!state) {
|
|
773
|
+
state = { pendingText: '', lastDeliveredKey: '' };
|
|
774
|
+
states.set(key, state);
|
|
775
|
+
}
|
|
776
|
+
return state;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function v2FlushAssistant(state, directory, sessionId) {
|
|
780
|
+
const text = state.pendingText.trim();
|
|
781
|
+
if (!text || text === state.lastDeliveredKey) return;
|
|
782
|
+
v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
|
|
783
|
+
state.lastDeliveredKey = text;
|
|
784
|
+
state.pendingText = '';
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function v2FilePath(file) {
|
|
788
|
+
if (typeof file === 'string') return file;
|
|
789
|
+
if (!file || typeof file !== 'object') return '';
|
|
790
|
+
return file.path ?? file.file ?? file.name ?? '';
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function v2HandleEvent(event, directory, states) {
|
|
794
|
+
if (!event || typeof event !== 'object') return;
|
|
795
|
+
const type = typeof event.type === 'string' ? event.type : '';
|
|
796
|
+
const data = v2EventData(event);
|
|
797
|
+
const sessionId = v2SessionId(event);
|
|
798
|
+
const state = v2SessionState(states, sessionId);
|
|
799
|
+
const run = (payload) => v2RunHook(payload, directory, sessionId);
|
|
800
|
+
|
|
801
|
+
switch (type) {
|
|
802
|
+
case 'session.created':
|
|
803
|
+
run({ hook_event_name: 'session.created' });
|
|
804
|
+
break;
|
|
805
|
+
case 'session.text.delta':
|
|
806
|
+
state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
|
|
807
|
+
break;
|
|
808
|
+
case 'session.message.updated':
|
|
809
|
+
case 'message.updated':
|
|
810
|
+
if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
|
|
811
|
+
break;
|
|
812
|
+
case 'session.step.ended': {
|
|
813
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
814
|
+
for (const file of files) {
|
|
815
|
+
const filePath = v2FilePath(file);
|
|
816
|
+
if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
|
|
817
|
+
}
|
|
818
|
+
if (typeof data.text === 'string') state.pendingText += data.text;
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
case 'session.idle':
|
|
822
|
+
v2FlushAssistant(state, directory, sessionId);
|
|
823
|
+
run({ hook_event_name: 'session.idle' });
|
|
824
|
+
break;
|
|
825
|
+
case 'session.compacted':
|
|
826
|
+
run({ hook_event_name: 'session.compacted' });
|
|
827
|
+
break;
|
|
828
|
+
default:
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function setup(ctx) {
|
|
834
|
+
const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
|
|
835
|
+
const controller = new AbortController();
|
|
836
|
+
const states = new Map();
|
|
837
|
+
|
|
838
|
+
if (ctx?.tool?.hook) {
|
|
839
|
+
try {
|
|
840
|
+
Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
|
|
841
|
+
const sessionId = v2SessionId(event);
|
|
842
|
+
const data = event && typeof event.input === 'object' ? event.input : {};
|
|
843
|
+
const tool = event?.tool ?? event?.name ?? '';
|
|
844
|
+
v2RunHook({
|
|
845
|
+
hook_event_name: 'tool.execute.after',
|
|
846
|
+
tool_name: tool,
|
|
847
|
+
tool_input: data,
|
|
848
|
+
}, directory, sessionId);
|
|
849
|
+
if (tool === 'shell') {
|
|
850
|
+
v2RunHook({
|
|
851
|
+
hook_event_name: 'command.executed',
|
|
852
|
+
command: data.command ?? data.cmd ?? '',
|
|
853
|
+
}, directory, sessionId);
|
|
854
|
+
}
|
|
855
|
+
})).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
|
|
856
|
+
} catch (error) {
|
|
857
|
+
v2ReportHookFailure('execute.after', error?.message ?? error);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
if (ctx?.event?.subscribe) {
|
|
862
|
+
void (async () => {
|
|
863
|
+
try {
|
|
864
|
+
const stream = ctx.event.subscribe({ signal: controller.signal });
|
|
865
|
+
for await (const event of stream) v2HandleEvent(event, directory, states);
|
|
866
|
+
} catch (error) {
|
|
867
|
+
if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
|
|
868
|
+
}
|
|
869
|
+
})();
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
return () => controller.abort();
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
export default { id: 'memorix', setup };
|
|
719
876
|
`;
|
|
720
877
|
}
|
|
721
878
|
|