@memnexus-ai/cli 1.7.162 → 1.7.164
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/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +52 -0
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/memories.d.ts.map +1 -1
- package/dist/commands/memories.js +11 -2
- package/dist/commands/memories.js.map +1 -1
- package/dist/lib/setup/hooks-writer.d.ts +51 -0
- package/dist/lib/setup/hooks-writer.d.ts.map +1 -0
- package/dist/lib/setup/hooks-writer.js +276 -0
- package/dist/lib/setup/hooks-writer.js.map +1 -0
- package/dist/lib/setup/index.d.ts +2 -0
- package/dist/lib/setup/index.d.ts.map +1 -1
- package/dist/lib/setup/index.js +4 -1
- package/dist/lib/setup/index.js.map +1 -1
- package/hooks/capture-precompact.sh +218 -0
- package/hooks/capture-session-end.sh +225 -0
- package/package.json +2 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hooks writer — install and remove MemNexus lifecycle hooks for AI agents.
|
|
4
|
+
*
|
|
5
|
+
* Phase 1 scope: Claude Code only.
|
|
6
|
+
*
|
|
7
|
+
* Copies bundled hook scripts to ~/.memnexus/hooks/ and merges hook entries
|
|
8
|
+
* into the agent's settings.json without overwriting existing hooks.
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.installHooks = installHooks;
|
|
15
|
+
exports.removeHooks = removeHooks;
|
|
16
|
+
const node_fs_1 = require("node:fs");
|
|
17
|
+
const promises_1 = require("node:fs/promises");
|
|
18
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
19
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
20
|
+
const detector_js_1 = require("./detector.js");
|
|
21
|
+
// Resolve the hooks/ directory bundled alongside the compiled output.
|
|
22
|
+
// Compiled file lives at dist/lib/setup/hooks-writer.js → go up 3 levels
|
|
23
|
+
// to the package root, then into hooks/.
|
|
24
|
+
// __dirname is a CommonJS global — available because module: "commonjs" in tsconfig.
|
|
25
|
+
const BUNDLED_HOOKS_DIR = node_path_1.default.resolve(__dirname, '../../../hooks');
|
|
26
|
+
/** Destination directory for installed hook scripts. */
|
|
27
|
+
const INSTALLED_HOOKS_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.memnexus', 'hooks');
|
|
28
|
+
/** Hook script filenames managed by MemNexus. */
|
|
29
|
+
const HOOK_SCRIPTS = ['capture-precompact.sh', 'capture-session-end.sh'];
|
|
30
|
+
/** Build hook entries with absolute paths (resolved at install time, not tilde). */
|
|
31
|
+
function buildMemnexusHooks() {
|
|
32
|
+
const hooksDir = node_path_1.default.join(node_os_1.default.homedir(), '.memnexus', 'hooks');
|
|
33
|
+
return {
|
|
34
|
+
PreCompact: [
|
|
35
|
+
{
|
|
36
|
+
matcher: '',
|
|
37
|
+
hooks: [
|
|
38
|
+
{
|
|
39
|
+
type: 'command',
|
|
40
|
+
command: `bash ${node_path_1.default.join(hooksDir, 'capture-precompact.sh')}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
Stop: [
|
|
46
|
+
{
|
|
47
|
+
hooks: [
|
|
48
|
+
{
|
|
49
|
+
type: 'command',
|
|
50
|
+
command: `bash ${node_path_1.default.join(hooksDir, 'capture-session-end.sh')}`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Marker string used to identify MemNexus hook entries. */
|
|
58
|
+
const MEMNEXUS_MARKER = 'memnexus';
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the settings.json path for a Claude Code installation.
|
|
61
|
+
* - global scope: ~/.claude/settings.json
|
|
62
|
+
* - project scope: <projectDir>/.claude/settings.json
|
|
63
|
+
*/
|
|
64
|
+
function resolveSettingsPath(scope, projectDir) {
|
|
65
|
+
if (scope === 'project') {
|
|
66
|
+
if (!projectDir) {
|
|
67
|
+
throw new Error('Project directory required for project scope');
|
|
68
|
+
}
|
|
69
|
+
return node_path_1.default.join(projectDir, '.claude', 'settings.json');
|
|
70
|
+
}
|
|
71
|
+
return (0, detector_js_1.resolvePath)('~/.claude/settings.json');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Return true if a hook entry array contains a MemNexus command.
|
|
75
|
+
*/
|
|
76
|
+
function arrayHasMemnexusHook(entries) {
|
|
77
|
+
return entries.some((entry) => {
|
|
78
|
+
if (typeof entry !== 'object' || entry === null)
|
|
79
|
+
return false;
|
|
80
|
+
const e = entry;
|
|
81
|
+
const subHooks = e.hooks;
|
|
82
|
+
if (!Array.isArray(subHooks))
|
|
83
|
+
return false;
|
|
84
|
+
return subHooks.some((h) => {
|
|
85
|
+
if (typeof h !== 'object' || h === null)
|
|
86
|
+
return false;
|
|
87
|
+
const cmd = h.command;
|
|
88
|
+
return typeof cmd === 'string' && cmd.includes(MEMNEXUS_MARKER);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Replace any existing MemNexus entry in a hook array, or append if not present.
|
|
94
|
+
*/
|
|
95
|
+
function upsertHookEntries(existing, newEntries) {
|
|
96
|
+
// Remove old memnexus entries
|
|
97
|
+
const filtered = existing.filter((entry) => {
|
|
98
|
+
if (typeof entry !== 'object' || entry === null)
|
|
99
|
+
return true;
|
|
100
|
+
const e = entry;
|
|
101
|
+
const subHooks = e.hooks;
|
|
102
|
+
if (!Array.isArray(subHooks))
|
|
103
|
+
return true;
|
|
104
|
+
return !subHooks.some((h) => {
|
|
105
|
+
if (typeof h !== 'object' || h === null)
|
|
106
|
+
return false;
|
|
107
|
+
const cmd = h.command;
|
|
108
|
+
return typeof cmd === 'string' && cmd.includes(MEMNEXUS_MARKER);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// Append the new entries
|
|
112
|
+
return [...filtered, ...newEntries];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Install MemNexus lifecycle hooks for a Claude Code installation.
|
|
116
|
+
*
|
|
117
|
+
* - Copies bundled hook scripts to ~/.memnexus/hooks/
|
|
118
|
+
* - Merges hook entries into the agent's settings.json
|
|
119
|
+
* - Idempotent: re-running updates scripts and entries in place
|
|
120
|
+
*
|
|
121
|
+
* Returns null for agents other than Claude Code (Phase 1 scope).
|
|
122
|
+
*/
|
|
123
|
+
async function installHooks(options) {
|
|
124
|
+
const { agent, scope, projectDir, dryRun } = options;
|
|
125
|
+
// Phase 1: Claude Code only
|
|
126
|
+
if (agent.id !== 'claude-code') {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const scriptsCopied = [];
|
|
130
|
+
// ── 1. Copy hook scripts to ~/.memnexus/hooks/ ──────────────────────────
|
|
131
|
+
if (!dryRun) {
|
|
132
|
+
await (0, promises_1.mkdir)(INSTALLED_HOOKS_DIR, { recursive: true });
|
|
133
|
+
for (const scriptName of HOOK_SCRIPTS) {
|
|
134
|
+
const src = node_path_1.default.join(BUNDLED_HOOKS_DIR, scriptName);
|
|
135
|
+
if (!(0, node_fs_1.existsSync)(src)) {
|
|
136
|
+
// Bundled script missing — skip gracefully (shouldn't happen in production)
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const dest = node_path_1.default.join(INSTALLED_HOOKS_DIR, scriptName);
|
|
140
|
+
await (0, promises_1.copyFile)(src, dest);
|
|
141
|
+
await (0, promises_1.chmod)(dest, 0o755);
|
|
142
|
+
scriptsCopied.push(scriptName);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Dry-run: report what would be copied
|
|
147
|
+
for (const scriptName of HOOK_SCRIPTS) {
|
|
148
|
+
const src = node_path_1.default.join(BUNDLED_HOOKS_DIR, scriptName);
|
|
149
|
+
if ((0, node_fs_1.existsSync)(src)) {
|
|
150
|
+
scriptsCopied.push(scriptName);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ── 2. Merge hook entries into settings.json ─────────────────────────────
|
|
155
|
+
const configPath = resolveSettingsPath(scope, projectDir);
|
|
156
|
+
let backupPath;
|
|
157
|
+
let action;
|
|
158
|
+
let settings = {};
|
|
159
|
+
const fileExists = (0, node_fs_1.existsSync)(configPath);
|
|
160
|
+
if (fileExists) {
|
|
161
|
+
try {
|
|
162
|
+
const raw = await (0, promises_1.readFile)(configPath, 'utf-8');
|
|
163
|
+
settings = JSON.parse(raw);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
throw new Error(`Cannot parse ${configPath} — fix the JSON syntax and retry. ` +
|
|
167
|
+
`Original error: ${err.message}`);
|
|
168
|
+
}
|
|
169
|
+
if (!dryRun) {
|
|
170
|
+
backupPath = configPath + '.bak';
|
|
171
|
+
await (0, promises_1.copyFile)(configPath, backupPath);
|
|
172
|
+
}
|
|
173
|
+
action = 'updated';
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
action = 'created';
|
|
177
|
+
}
|
|
178
|
+
// Ensure hooks object exists
|
|
179
|
+
if (!settings.hooks || typeof settings.hooks !== 'object') {
|
|
180
|
+
settings.hooks = {};
|
|
181
|
+
}
|
|
182
|
+
const hooks = settings.hooks;
|
|
183
|
+
// Merge PreCompact hooks
|
|
184
|
+
const preCompactEntries = Array.isArray(hooks.PreCompact) ? hooks.PreCompact : [];
|
|
185
|
+
hooks.PreCompact = upsertHookEntries(preCompactEntries, buildMemnexusHooks().PreCompact);
|
|
186
|
+
// Merge Stop hooks
|
|
187
|
+
const stopEntries = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
188
|
+
hooks.Stop = upsertHookEntries(stopEntries, buildMemnexusHooks().Stop);
|
|
189
|
+
// Check whether anything actually changed when updating
|
|
190
|
+
if (fileExists &&
|
|
191
|
+
action === 'updated' &&
|
|
192
|
+
Array.isArray(hooks.PreCompact) &&
|
|
193
|
+
Array.isArray(hooks.Stop) &&
|
|
194
|
+
arrayHasMemnexusHook(preCompactEntries) &&
|
|
195
|
+
arrayHasMemnexusHook(stopEntries) &&
|
|
196
|
+
JSON.stringify(hooks.PreCompact) === JSON.stringify(preCompactEntries) &&
|
|
197
|
+
JSON.stringify(hooks.Stop) === JSON.stringify(stopEntries)) {
|
|
198
|
+
// Nothing changed — already up to date
|
|
199
|
+
action = 'skipped';
|
|
200
|
+
backupPath = undefined;
|
|
201
|
+
}
|
|
202
|
+
if (!dryRun && action !== 'skipped') {
|
|
203
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(configPath), { recursive: true });
|
|
204
|
+
await (0, promises_1.writeFile)(configPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
205
|
+
}
|
|
206
|
+
return { scriptsDir: INSTALLED_HOOKS_DIR, scriptsCopied, configPath, backupPath, action };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Remove MemNexus lifecycle hooks for a Claude Code installation.
|
|
210
|
+
*
|
|
211
|
+
* - Removes hook entries containing "memnexus" from settings.json
|
|
212
|
+
* - Deletes hook scripts from ~/.memnexus/hooks/
|
|
213
|
+
* - Cleans up empty hook arrays
|
|
214
|
+
*
|
|
215
|
+
* No-ops for agents other than Claude Code.
|
|
216
|
+
*/
|
|
217
|
+
async function removeHooks(options) {
|
|
218
|
+
const { agent, scope, projectDir } = options;
|
|
219
|
+
// Phase 1: Claude Code only
|
|
220
|
+
if (agent.id !== 'claude-code') {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// ── 1. Remove hook scripts ───────────────────────────────────────────────
|
|
224
|
+
for (const scriptName of HOOK_SCRIPTS) {
|
|
225
|
+
const scriptPath = node_path_1.default.join(INSTALLED_HOOKS_DIR, scriptName);
|
|
226
|
+
if ((0, node_fs_1.existsSync)(scriptPath)) {
|
|
227
|
+
await (0, promises_1.rm)(scriptPath, { force: true });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ── 2. Remove hook entries from settings.json ────────────────────────────
|
|
231
|
+
const configPath = resolveSettingsPath(scope, projectDir);
|
|
232
|
+
if (!(0, node_fs_1.existsSync)(configPath))
|
|
233
|
+
return;
|
|
234
|
+
let settings;
|
|
235
|
+
try {
|
|
236
|
+
const raw = await (0, promises_1.readFile)(configPath, 'utf-8');
|
|
237
|
+
settings = JSON.parse(raw);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return; // Unparseable — leave it alone
|
|
241
|
+
}
|
|
242
|
+
if (!settings.hooks || typeof settings.hooks !== 'object')
|
|
243
|
+
return;
|
|
244
|
+
const hooks = settings.hooks;
|
|
245
|
+
for (const eventType of ['PreCompact', 'Stop']) {
|
|
246
|
+
const entries = hooks[eventType];
|
|
247
|
+
if (!Array.isArray(entries))
|
|
248
|
+
continue;
|
|
249
|
+
const filtered = entries.filter((entry) => {
|
|
250
|
+
if (typeof entry !== 'object' || entry === null)
|
|
251
|
+
return true;
|
|
252
|
+
const e = entry;
|
|
253
|
+
const subHooks = e.hooks;
|
|
254
|
+
if (!Array.isArray(subHooks))
|
|
255
|
+
return true;
|
|
256
|
+
return !subHooks.some((h) => {
|
|
257
|
+
if (typeof h !== 'object' || h === null)
|
|
258
|
+
return false;
|
|
259
|
+
const cmd = h.command;
|
|
260
|
+
return typeof cmd === 'string' && cmd.includes(MEMNEXUS_MARKER);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
if (filtered.length === 0) {
|
|
264
|
+
delete hooks[eventType];
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
hooks[eventType] = filtered;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// If hooks object is now empty, remove it
|
|
271
|
+
if (Object.keys(hooks).length === 0) {
|
|
272
|
+
delete settings.hooks;
|
|
273
|
+
}
|
|
274
|
+
await (0, promises_1.writeFile)(configPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=hooks-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-writer.js","sourceRoot":"","sources":["../../../src/lib/setup/hooks-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,5 +6,7 @@ export { composeRules, parseSteeringVersion } from './rules-templates.js';
|
|
|
6
6
|
export { getAgent, getAgentIds, getSortedAgents, AGENTS } from './registry.js';
|
|
7
7
|
export { writeAllSkills, removeAllSkills, listSkills, resolveSkillsDir, resolveSkillPath, } from './skills-writer.js';
|
|
8
8
|
export { SKILLS, parseSkillVersion } from './skills-templates.js';
|
|
9
|
+
export { installHooks, removeHooks } from './hooks-writer.js';
|
|
10
|
+
export type { HooksWriteResult } from './hooks-writer.js';
|
|
9
11
|
export type * from './types.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/setup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClE,mBAAmB,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/setup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,mBAAmB,YAAY,CAAC"}
|
package/dist/lib/setup/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseSkillVersion = exports.SKILLS = exports.resolveSkillPath = exports.resolveSkillsDir = exports.listSkills = exports.removeAllSkills = exports.writeAllSkills = exports.AGENTS = exports.getSortedAgents = exports.getAgentIds = exports.getAgent = exports.parseSteeringVersion = exports.composeRules = exports.resolveSteeringPath = exports.getSteeringVersion = exports.removeSteeringSection = exports.writeSteeringFile = exports.verifyAll = exports.verifyAgent = exports.resolveConfigPath = exports.removeAgentConfig = exports.writeAgentConfig = exports.resolvePath = exports.detectAgents = void 0;
|
|
3
|
+
exports.removeHooks = exports.installHooks = exports.parseSkillVersion = exports.SKILLS = exports.resolveSkillPath = exports.resolveSkillsDir = exports.listSkills = exports.removeAllSkills = exports.writeAllSkills = exports.AGENTS = exports.getSortedAgents = exports.getAgentIds = exports.getAgent = exports.parseSteeringVersion = exports.composeRules = exports.resolveSteeringPath = exports.getSteeringVersion = exports.removeSteeringSection = exports.writeSteeringFile = exports.verifyAll = exports.verifyAgent = exports.resolveConfigPath = exports.removeAgentConfig = exports.writeAgentConfig = exports.resolvePath = exports.detectAgents = void 0;
|
|
4
4
|
var detector_js_1 = require("./detector.js");
|
|
5
5
|
Object.defineProperty(exports, "detectAgents", { enumerable: true, get: function () { return detector_js_1.detectAgents; } });
|
|
6
6
|
Object.defineProperty(exports, "resolvePath", { enumerable: true, get: function () { return detector_js_1.resolvePath; } });
|
|
@@ -33,4 +33,7 @@ Object.defineProperty(exports, "resolveSkillPath", { enumerable: true, get: func
|
|
|
33
33
|
var skills_templates_js_1 = require("./skills-templates.js");
|
|
34
34
|
Object.defineProperty(exports, "SKILLS", { enumerable: true, get: function () { return skills_templates_js_1.SKILLS; } });
|
|
35
35
|
Object.defineProperty(exports, "parseSkillVersion", { enumerable: true, get: function () { return skills_templates_js_1.parseSkillVersion; } });
|
|
36
|
+
var hooks_writer_js_1 = require("./hooks-writer.js");
|
|
37
|
+
Object.defineProperty(exports, "installHooks", { enumerable: true, get: function () { return hooks_writer_js_1.installHooks; } });
|
|
38
|
+
Object.defineProperty(exports, "removeHooks", { enumerable: true, get: function () { return hooks_writer_js_1.removeHooks; } });
|
|
36
39
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/setup/index.ts"],"names":[],"mappings":";;;AAAA,6CAA0D;AAAjD,2GAAA,YAAY,OAAA;AAAE,0GAAA,WAAW,OAAA;AAClC,uDAA4F;AAAnF,oHAAA,gBAAgB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAC/D,6CAAuD;AAA9C,0GAAA,WAAW,OAAA;AAAE,wGAAA,SAAS,OAAA;AAC/B,qDAK2B;AAJzB,oHAAA,iBAAiB,OAAA;AACjB,wHAAA,qBAAqB,OAAA;AACrB,qHAAA,kBAAkB,OAAA;AAClB,sHAAA,mBAAmB,OAAA;AAErB,2DAA0E;AAAjE,kHAAA,YAAY,OAAA;AAAE,0HAAA,oBAAoB,OAAA;AAC3C,6CAA+E;AAAtE,uGAAA,QAAQ,OAAA;AAAE,0GAAA,WAAW,OAAA;AAAE,8GAAA,eAAe,OAAA;AAAE,qGAAA,MAAM,OAAA;AACvD,uDAM4B;AAL1B,kHAAA,cAAc,OAAA;AACd,mHAAA,eAAe,OAAA;AACf,8GAAA,UAAU,OAAA;AACV,oHAAA,gBAAgB,OAAA;AAChB,oHAAA,gBAAgB,OAAA;AAElB,6DAAkE;AAAzD,6GAAA,MAAM,OAAA;AAAE,wHAAA,iBAAiB,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/setup/index.ts"],"names":[],"mappings":";;;AAAA,6CAA0D;AAAjD,2GAAA,YAAY,OAAA;AAAE,0GAAA,WAAW,OAAA;AAClC,uDAA4F;AAAnF,oHAAA,gBAAgB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAC/D,6CAAuD;AAA9C,0GAAA,WAAW,OAAA;AAAE,wGAAA,SAAS,OAAA;AAC/B,qDAK2B;AAJzB,oHAAA,iBAAiB,OAAA;AACjB,wHAAA,qBAAqB,OAAA;AACrB,qHAAA,kBAAkB,OAAA;AAClB,sHAAA,mBAAmB,OAAA;AAErB,2DAA0E;AAAjE,kHAAA,YAAY,OAAA;AAAE,0HAAA,oBAAoB,OAAA;AAC3C,6CAA+E;AAAtE,uGAAA,QAAQ,OAAA;AAAE,0GAAA,WAAW,OAAA;AAAE,8GAAA,eAAe,OAAA;AAAE,qGAAA,MAAM,OAAA;AACvD,uDAM4B;AAL1B,kHAAA,cAAc,OAAA;AACd,mHAAA,eAAe,OAAA;AACf,8GAAA,UAAU,OAAA;AACV,oHAAA,gBAAgB,OAAA;AAChB,oHAAA,gBAAgB,OAAA;AAElB,6DAAkE;AAAzD,6GAAA,MAAM,OAAA;AAAE,wHAAA,iBAAiB,OAAA;AAClC,qDAA8D;AAArD,+GAAA,YAAY,OAAA;AAAE,8GAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# capture-precompact.sh — Automatic memory capture before context compaction
|
|
3
|
+
#
|
|
4
|
+
# Installed at ~/.memnexus/hooks/ by `mx mcp install` for end-user lifecycle
|
|
5
|
+
# hook integration with Claude Code.
|
|
6
|
+
#
|
|
7
|
+
# The highest-value automatic capture signal: fires when ~95% of context window
|
|
8
|
+
# is full, meaning rich context (decisions, debugging sessions, architecture
|
|
9
|
+
# discussions) is about to be lost forever.
|
|
10
|
+
#
|
|
11
|
+
# Unlike session-end capture (which summarises what was done), this hook
|
|
12
|
+
# extracts structured knowledge — decisions, problems solved, patterns — and
|
|
13
|
+
# saves it as a permanent memory before compaction discards the context.
|
|
14
|
+
#
|
|
15
|
+
# Fires on: PreCompact
|
|
16
|
+
# Input: JSON on stdin with transcript_path, session_id, trigger
|
|
17
|
+
# Output: none (side-effect only — saves memory via mx CLI)
|
|
18
|
+
# Exit: always 0 (never blocks compaction)
|
|
19
|
+
|
|
20
|
+
# Fail-open: any error -> allow compaction without saving
|
|
21
|
+
trap 'exit 0' ERR
|
|
22
|
+
|
|
23
|
+
# ── 1. Check required tools ──────────────────────────────────────────────
|
|
24
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
25
|
+
command -v mx >/dev/null 2>&1 || exit 0
|
|
26
|
+
|
|
27
|
+
# ── 2. Parse hook input ──────────────────────────────────────────────────
|
|
28
|
+
INPUT=$(cat)
|
|
29
|
+
|
|
30
|
+
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
31
|
+
_SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
32
|
+
|
|
33
|
+
# Skip if no transcript
|
|
34
|
+
if [[ -z "$TRANSCRIPT_PATH" || ! -f "$TRANSCRIPT_PATH" ]]; then
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Validate transcript path — must resolve to a real file, not a symlink to elsewhere
|
|
39
|
+
REAL_TRANSCRIPT=$(realpath "$TRANSCRIPT_PATH" 2>/dev/null) || exit 0
|
|
40
|
+
if [[ ! -f "$REAL_TRANSCRIPT" ]]; then
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
TRANSCRIPT_PATH="$REAL_TRANSCRIPT"
|
|
44
|
+
|
|
45
|
+
# ── 3. Derive project name from transcript path or cwd ───────────────────
|
|
46
|
+
# Transcript paths typically contain the project directory name
|
|
47
|
+
PROJECT_NAME=$(basename "$(dirname "$TRANSCRIPT_PATH")" 2>/dev/null)
|
|
48
|
+
if [[ -z "$PROJECT_NAME" || "$PROJECT_NAME" == "." ]]; then
|
|
49
|
+
PROJECT_NAME=$(basename "$(pwd)" 2>/dev/null || echo "unknown")
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# ── 4. Extract structured data from transcript ───────────────────────────
|
|
53
|
+
|
|
54
|
+
tmpdir=$(mktemp -d)
|
|
55
|
+
trap 'rm -rf "$tmpdir"; exit 0' EXIT ERR
|
|
56
|
+
|
|
57
|
+
# Total line count (proxy for session depth)
|
|
58
|
+
_TOTAL_LINES=$(wc -l < "$TRANSCRIPT_PATH" 2>/dev/null || echo "0")
|
|
59
|
+
|
|
60
|
+
# Files modified (Write/Edit tool calls)
|
|
61
|
+
FILES_MODIFIED=$(jq -r '
|
|
62
|
+
select(.type == "assistant") |
|
|
63
|
+
.message.content[]? |
|
|
64
|
+
select(.type == "tool_use" and (.name == "Write" or .name == "Edit")) |
|
|
65
|
+
.input.file_path // empty
|
|
66
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | grep -v '^$' | sort -u | head -30)
|
|
67
|
+
|
|
68
|
+
# Files read (Read tool calls — indicates areas of investigation)
|
|
69
|
+
FILES_READ=$(jq -r '
|
|
70
|
+
select(.type == "assistant") |
|
|
71
|
+
.message.content[]? |
|
|
72
|
+
select(.type == "tool_use" and .name == "Read") |
|
|
73
|
+
.input.file_path // empty
|
|
74
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | grep -v '^$' | sort | uniq -c | sort -rn | head -15 | awk '{print $2}')
|
|
75
|
+
|
|
76
|
+
# Bash commands run (significant actions)
|
|
77
|
+
BASH_COMMANDS=$(jq -r '
|
|
78
|
+
select(.type == "assistant") |
|
|
79
|
+
.message.content[]? |
|
|
80
|
+
select(.type == "tool_use" and .name == "Bash") |
|
|
81
|
+
.input.command // empty
|
|
82
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | grep -v '^$' | grep -vE '^(ls|pwd|echo|cat |head |tail )' | tail -20)
|
|
83
|
+
|
|
84
|
+
# Git activity
|
|
85
|
+
GIT_COMMITS=$(echo "$BASH_COMMANDS" | grep -oE 'git commit' | wc -l | tr -d ' ')
|
|
86
|
+
GIT_PUSHES=$(echo "$BASH_COMMANDS" | grep -oE 'git push' | wc -l | tr -d ' ')
|
|
87
|
+
PR_CREATES=$(echo "$BASH_COMMANDS" | grep -oE 'gh pr create' | wc -l | tr -d ' ')
|
|
88
|
+
PR_MERGES=$(echo "$BASH_COMMANDS" | grep -oE 'gh pr merge' | wc -l | tr -d ' ')
|
|
89
|
+
|
|
90
|
+
# PR and issue references from all text content
|
|
91
|
+
ALL_TEXT=$(jq -r '
|
|
92
|
+
select(.type == "user" or .type == "assistant") |
|
|
93
|
+
.message.content |
|
|
94
|
+
if type == "array" then
|
|
95
|
+
[.[] | select(.type == "text") | .text] | join("\n")
|
|
96
|
+
elif type == "string" then .
|
|
97
|
+
else empty end
|
|
98
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null)
|
|
99
|
+
|
|
100
|
+
GH_REFS=$(echo "$ALL_TEXT" | grep -oE '(PR |#)[0-9]+' 2>/dev/null | sort -u | head -15 | tr '\n' ', ' | sed 's/,$//')
|
|
101
|
+
|
|
102
|
+
# User messages (to understand intent and topics)
|
|
103
|
+
USER_MESSAGES=$(jq -r '
|
|
104
|
+
select(.type == "user") |
|
|
105
|
+
.message.content |
|
|
106
|
+
if type == "array" then
|
|
107
|
+
[.[] | select(.type == "text") | .text] | join("\n")
|
|
108
|
+
elif type == "string" then .
|
|
109
|
+
else empty end
|
|
110
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | head -c 3000)
|
|
111
|
+
|
|
112
|
+
# Last few assistant text blocks (conclusions/results)
|
|
113
|
+
RECENT_RESULTS=$(tail -100 "$TRANSCRIPT_PATH" | jq -r '
|
|
114
|
+
select(.type == "assistant") |
|
|
115
|
+
.message.content |
|
|
116
|
+
if type == "array" then
|
|
117
|
+
[.[] | select(.type == "text") | .text] | join("\n")
|
|
118
|
+
elif type == "string" then .
|
|
119
|
+
else empty end
|
|
120
|
+
' 2>/dev/null | tail -c 2000)
|
|
121
|
+
|
|
122
|
+
# Tool usage summary
|
|
123
|
+
TOOL_SUMMARY=$(jq -r '
|
|
124
|
+
select(.type == "assistant") |
|
|
125
|
+
.message.content[]? |
|
|
126
|
+
select(.type == "tool_use") | .name
|
|
127
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | sort | uniq -c | sort -rn | head -10 | awk '{printf "%s(%d) ", $2, $1}')
|
|
128
|
+
|
|
129
|
+
# ── 5. Significance filter ───────────────────────────────────────────────
|
|
130
|
+
# Skip if session was trivial (very few tool calls, no files modified)
|
|
131
|
+
TOOL_COUNT=$(jq -r '
|
|
132
|
+
select(.type == "assistant") |
|
|
133
|
+
.message.content[]? |
|
|
134
|
+
select(.type == "tool_use") | .name
|
|
135
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null | wc -l | tr -d ' ')
|
|
136
|
+
|
|
137
|
+
if [[ "$TOOL_COUNT" -lt 5 && -z "$FILES_MODIFIED" ]]; then
|
|
138
|
+
# Trivial session — not worth capturing
|
|
139
|
+
exit 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# ── 5b. Sanitize sensitive data ──────────────────────────────────────────
|
|
143
|
+
# Strip common secret patterns from extracted text before saving as memory
|
|
144
|
+
sanitize() {
|
|
145
|
+
sed -E \
|
|
146
|
+
-e 's/(api[_-]?key|token|secret|password|bearer|authorization)[[:space:]]*[:=][[:space:]]*[^[:space:],\"'"'"']+/\1=<REDACTED>/gi' \
|
|
147
|
+
-e 's/cmk_live_[A-Za-z0-9_-]+/cmk_live_<REDACTED>/g' \
|
|
148
|
+
-e 's/sk-[A-Za-z0-9_-]{20,}/sk-<REDACTED>/g' \
|
|
149
|
+
-e 's/ghp_[A-Za-z0-9]{36}/ghp_<REDACTED>/g' \
|
|
150
|
+
-e 's/gho_[A-Za-z0-9]{36}/gho_<REDACTED>/g' \
|
|
151
|
+
-e 's/-----BEGIN [A-Z ]*PRIVATE KEY-----/-----BEGIN REDACTED KEY-----/g'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
USER_MESSAGES=$(echo "$USER_MESSAGES" | sanitize)
|
|
155
|
+
BASH_COMMANDS=$(echo "$BASH_COMMANDS" | sanitize)
|
|
156
|
+
RECENT_RESULTS=$(echo "$RECENT_RESULTS" | sanitize)
|
|
157
|
+
|
|
158
|
+
# ── 6. Build structured memory content ───────────────────────────────────
|
|
159
|
+
GIT_BRANCH=$(git -C "$(dirname "$TRANSCRIPT_PATH")" branch --show-current 2>/dev/null || \
|
|
160
|
+
git branch --show-current 2>/dev/null || echo "unknown")
|
|
161
|
+
|
|
162
|
+
# Build files list
|
|
163
|
+
MOD_LIST=""
|
|
164
|
+
if [[ -n "$FILES_MODIFIED" ]]; then
|
|
165
|
+
MOD_LIST=$(echo "$FILES_MODIFIED" | sed 's/^/- /')
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
READ_LIST=""
|
|
169
|
+
if [[ -n "$FILES_READ" ]]; then
|
|
170
|
+
READ_LIST=$(echo "$FILES_READ" | head -10 | sed 's/^/- /')
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Build activity summary
|
|
174
|
+
ACTIVITY=""
|
|
175
|
+
[[ "$GIT_COMMITS" -gt 0 ]] && ACTIVITY="${ACTIVITY}${GIT_COMMITS} commits, "
|
|
176
|
+
[[ "$GIT_PUSHES" -gt 0 ]] && ACTIVITY="${ACTIVITY}${GIT_PUSHES} pushes, "
|
|
177
|
+
[[ "$PR_CREATES" -gt 0 ]] && ACTIVITY="${ACTIVITY}${PR_CREATES} PRs created, "
|
|
178
|
+
[[ "$PR_MERGES" -gt 0 ]] && ACTIVITY="${ACTIVITY}${PR_MERGES} PRs merged, "
|
|
179
|
+
ACTIVITY="${ACTIVITY}${TOOL_COUNT} tool calls"
|
|
180
|
+
|
|
181
|
+
CONTENT="[Auto-Captured] Session context before compaction — ${PROJECT_NAME}
|
|
182
|
+
|
|
183
|
+
Branch: ${GIT_BRANCH}
|
|
184
|
+
Activity: ${ACTIVITY}
|
|
185
|
+
GitHub refs: ${GH_REFS:-none}
|
|
186
|
+
|
|
187
|
+
Files modified:
|
|
188
|
+
${MOD_LIST:-None}
|
|
189
|
+
|
|
190
|
+
Files investigated:
|
|
191
|
+
${READ_LIST:-None}
|
|
192
|
+
|
|
193
|
+
Tools used: ${TOOL_SUMMARY}
|
|
194
|
+
|
|
195
|
+
What the user was working on:
|
|
196
|
+
$(echo "$USER_MESSAGES" | head -c 1500)
|
|
197
|
+
|
|
198
|
+
Recent results/conclusions:
|
|
199
|
+
$(echo "$RECENT_RESULTS" | head -c 1500)"
|
|
200
|
+
|
|
201
|
+
# Truncate total content to ~5000 chars
|
|
202
|
+
CONTENT=$(echo "$CONTENT" | head -c 5000)
|
|
203
|
+
|
|
204
|
+
# ── 7. Save memory (with timeout — never block compaction) ───────────────
|
|
205
|
+
TIMEOUT_CMD=""
|
|
206
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
207
|
+
TIMEOUT_CMD="timeout 10"
|
|
208
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
209
|
+
TIMEOUT_CMD="gtimeout 10"
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
$TIMEOUT_CMD mx memories create \
|
|
213
|
+
--conversation-id "NEW" \
|
|
214
|
+
--content "$CONTENT" \
|
|
215
|
+
--topics "auto-captured,precompact" \
|
|
216
|
+
>/dev/null 2>&1 || true
|
|
217
|
+
|
|
218
|
+
exit 0
|