memorix 1.1.12 → 1.1.13
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 +9 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/cli/index.js +331 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +9 -0
- package/dist/memcode-runtime/package.json +1 -0
- package/dist/sdk.js +1 -1
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +1 -1
- package/docs/DEVELOPMENT.md +3 -10
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/SETUP.md +3 -1
- package/docs/dev-log/progress.txt +33 -61
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/commands/agent-integrations.ts +384 -2
- package/src/cli/commands/setup.ts +13 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
1
2
|
import { existsSync } from 'node:fs';
|
|
2
3
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
4
|
import { basename, dirname, resolve } from 'node:path';
|
|
@@ -9,9 +10,12 @@ import {
|
|
|
9
10
|
buildMemorixServer,
|
|
10
11
|
getMcpAdapter,
|
|
11
12
|
getSetupAgentTargets,
|
|
13
|
+
installPluginPackage,
|
|
12
14
|
installMcpConfig,
|
|
15
|
+
tryInstallCodexPlugin,
|
|
13
16
|
type McpConfigAgent,
|
|
14
17
|
} from './setup.js';
|
|
18
|
+
import { getCliVersion } from '../version.js';
|
|
15
19
|
|
|
16
20
|
export type AgentIntegrationScope = 'local' | 'project' | 'global' | 'all';
|
|
17
21
|
export type AgentIntegrationStatus = 'ok' | 'missing' | 'repairable' | 'skipped';
|
|
@@ -40,6 +44,35 @@ export interface AgentGuidanceCheck {
|
|
|
40
44
|
issues: string[];
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
export interface AgentPluginCheck {
|
|
48
|
+
scope: 'global';
|
|
49
|
+
kind: 'bundle' | 'marketplace' | 'runtime' | 'hook-trust';
|
|
50
|
+
path: string;
|
|
51
|
+
exists: boolean;
|
|
52
|
+
status: AgentIntegrationStatus;
|
|
53
|
+
issues: string[];
|
|
54
|
+
version?: string;
|
|
55
|
+
hooks?: {
|
|
56
|
+
declared: string[];
|
|
57
|
+
expected: string[];
|
|
58
|
+
};
|
|
59
|
+
runtime?: {
|
|
60
|
+
installed: boolean;
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
version?: string;
|
|
63
|
+
};
|
|
64
|
+
hookTrust?: {
|
|
65
|
+
trusted: string[];
|
|
66
|
+
expected: string[];
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface AgentPluginStatus {
|
|
71
|
+
status: AgentIntegrationStatus;
|
|
72
|
+
issues: string[];
|
|
73
|
+
checks: AgentPluginCheck[];
|
|
74
|
+
}
|
|
75
|
+
|
|
43
76
|
export interface AgentIntegrationEntry {
|
|
44
77
|
agent: AgentName;
|
|
45
78
|
mcp: {
|
|
@@ -52,6 +85,7 @@ export interface AgentIntegrationEntry {
|
|
|
52
85
|
issues: string[];
|
|
53
86
|
checks: AgentGuidanceCheck[];
|
|
54
87
|
};
|
|
88
|
+
plugin: AgentPluginStatus;
|
|
55
89
|
}
|
|
56
90
|
|
|
57
91
|
export interface AgentIntegrationReport {
|
|
@@ -89,6 +123,12 @@ const GUIDANCE_AGENTS = new Set<AgentName>([
|
|
|
89
123
|
'trae',
|
|
90
124
|
]);
|
|
91
125
|
|
|
126
|
+
const CODEX_PLUGIN_NAME = 'memorix';
|
|
127
|
+
const CODEX_PLUGIN_MARKETPLACE = 'personal';
|
|
128
|
+
const CODEX_PLUGIN_ID = `${CODEX_PLUGIN_NAME}@${CODEX_PLUGIN_MARKETPLACE}`;
|
|
129
|
+
const CODEX_HOOK_EVENTS = ['SessionStart', 'UserPromptSubmit', 'PostToolUse', 'PreCompact', 'Stop'];
|
|
130
|
+
const CODEX_HOOK_STATE_NAMES = ['session_start', 'user_prompt_submit', 'post_tool_use', 'pre_compact', 'stop'];
|
|
131
|
+
|
|
92
132
|
type ConcreteAgentIntegrationScope = Exclude<AgentIntegrationScope, 'all'>;
|
|
93
133
|
type JsonRecord = Record<string, unknown>;
|
|
94
134
|
|
|
@@ -179,6 +219,310 @@ function asRecord(value: unknown): JsonRecord | null {
|
|
|
179
219
|
return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : null;
|
|
180
220
|
}
|
|
181
221
|
|
|
222
|
+
function asRecordArray(value: unknown): JsonRecord[] {
|
|
223
|
+
return Array.isArray(value)
|
|
224
|
+
? value.map(asRecord).filter((entry): entry is JsonRecord => entry !== null)
|
|
225
|
+
: [];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function codexPluginPath(): string {
|
|
229
|
+
return `${homedir()}/.codex/plugins/${CODEX_PLUGIN_NAME}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function codexMarketplacePath(): string {
|
|
233
|
+
return `${homedir()}/.agents/plugins/marketplace.json`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function codexConfigPath(): string {
|
|
237
|
+
return `${homedir()}/.codex/config.toml`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function normalizeMarketplacePath(value: string): string {
|
|
241
|
+
return value.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function isMemorixCodexPlugin(value: JsonRecord): boolean {
|
|
245
|
+
return value.pluginId === CODEX_PLUGIN_ID
|
|
246
|
+
|| (value.name === CODEX_PLUGIN_NAME && value.marketplaceName === CODEX_PLUGIN_MARKETPLACE);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function parseCodexPluginList(output: string): AgentPluginCheck['runtime'] | null {
|
|
250
|
+
try {
|
|
251
|
+
const parsed = asRecord(JSON.parse(output));
|
|
252
|
+
if (!parsed) return null;
|
|
253
|
+
const entries = [...asRecordArray(parsed.installed), ...asRecordArray(parsed.available)];
|
|
254
|
+
const plugin = entries.find(isMemorixCodexPlugin);
|
|
255
|
+
if (!plugin) return { installed: false, enabled: false };
|
|
256
|
+
return {
|
|
257
|
+
installed: plugin.installed === true,
|
|
258
|
+
enabled: plugin.enabled === true,
|
|
259
|
+
...(typeof plugin.version === 'string' ? { version: plugin.version } : {}),
|
|
260
|
+
};
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function inspectCodexPluginBundle(): Promise<AgentPluginCheck> {
|
|
267
|
+
const pluginPath = codexPluginPath();
|
|
268
|
+
const manifestPath = `${pluginPath}/.codex-plugin/plugin.json`;
|
|
269
|
+
const hooksPath = `${pluginPath}/hooks/hooks.json`;
|
|
270
|
+
if (!existsSync(pluginPath)) {
|
|
271
|
+
return {
|
|
272
|
+
scope: 'global',
|
|
273
|
+
kind: 'bundle',
|
|
274
|
+
path: pluginPath,
|
|
275
|
+
exists: false,
|
|
276
|
+
status: 'missing',
|
|
277
|
+
issues: ['codex-plugin-bundle-missing'],
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (!existsSync(manifestPath)) {
|
|
281
|
+
return {
|
|
282
|
+
scope: 'global',
|
|
283
|
+
kind: 'bundle',
|
|
284
|
+
path: manifestPath,
|
|
285
|
+
exists: false,
|
|
286
|
+
status: 'repairable',
|
|
287
|
+
issues: ['codex-plugin-manifest-missing'],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let manifest: JsonRecord | null = null;
|
|
292
|
+
try {
|
|
293
|
+
manifest = asRecord(JSON.parse(await readFile(manifestPath, 'utf-8')));
|
|
294
|
+
} catch {
|
|
295
|
+
return {
|
|
296
|
+
scope: 'global',
|
|
297
|
+
kind: 'bundle',
|
|
298
|
+
path: manifestPath,
|
|
299
|
+
exists: true,
|
|
300
|
+
status: 'repairable',
|
|
301
|
+
issues: ['codex-plugin-manifest-unreadable'],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (!manifest || manifest.name !== CODEX_PLUGIN_NAME) {
|
|
305
|
+
return {
|
|
306
|
+
scope: 'global',
|
|
307
|
+
kind: 'bundle',
|
|
308
|
+
path: manifestPath,
|
|
309
|
+
exists: true,
|
|
310
|
+
status: 'repairable',
|
|
311
|
+
issues: ['codex-plugin-manifest-invalid'],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const version = typeof manifest.version === 'string' ? manifest.version : undefined;
|
|
316
|
+
const issues: string[] = [];
|
|
317
|
+
if (version !== getCliVersion()) issues.push('codex-plugin-version-mismatch');
|
|
318
|
+
if (manifest.hooks !== './hooks/hooks.json') issues.push('codex-hook-manifest-missing');
|
|
319
|
+
|
|
320
|
+
let declared: string[] = [];
|
|
321
|
+
if (!existsSync(hooksPath)) {
|
|
322
|
+
issues.push('codex-hook-manifest-missing');
|
|
323
|
+
} else {
|
|
324
|
+
try {
|
|
325
|
+
const hooksConfig = asRecord(JSON.parse(await readFile(hooksPath, 'utf-8')));
|
|
326
|
+
const hooks = asRecord(hooksConfig?.hooks);
|
|
327
|
+
declared = hooks ? Object.keys(hooks) : [];
|
|
328
|
+
if (CODEX_HOOK_EVENTS.some((event) => !declared.includes(event))) {
|
|
329
|
+
issues.push('codex-hook-events-missing');
|
|
330
|
+
}
|
|
331
|
+
} catch {
|
|
332
|
+
issues.push('codex-hook-manifest-unreadable');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
scope: 'global',
|
|
338
|
+
kind: 'bundle',
|
|
339
|
+
path: pluginPath,
|
|
340
|
+
exists: true,
|
|
341
|
+
status: issues.length > 0 ? 'repairable' : 'ok',
|
|
342
|
+
issues: unique(issues),
|
|
343
|
+
...(version ? { version } : {}),
|
|
344
|
+
hooks: { declared, expected: [...CODEX_HOOK_EVENTS] },
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function inspectCodexMarketplace(): Promise<AgentPluginCheck> {
|
|
349
|
+
const marketplacePath = codexMarketplacePath();
|
|
350
|
+
if (!existsSync(marketplacePath)) {
|
|
351
|
+
return {
|
|
352
|
+
scope: 'global',
|
|
353
|
+
kind: 'marketplace',
|
|
354
|
+
path: marketplacePath,
|
|
355
|
+
exists: false,
|
|
356
|
+
status: 'missing',
|
|
357
|
+
issues: ['codex-marketplace-missing'],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let catalog: JsonRecord | null = null;
|
|
362
|
+
try {
|
|
363
|
+
catalog = asRecord(JSON.parse(await readFile(marketplacePath, 'utf-8')));
|
|
364
|
+
} catch {
|
|
365
|
+
return {
|
|
366
|
+
scope: 'global',
|
|
367
|
+
kind: 'marketplace',
|
|
368
|
+
path: marketplacePath,
|
|
369
|
+
exists: true,
|
|
370
|
+
status: 'repairable',
|
|
371
|
+
issues: ['codex-marketplace-unreadable'],
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const entry = asRecordArray(catalog?.plugins).find((plugin) => plugin.name === CODEX_PLUGIN_NAME);
|
|
376
|
+
if (!entry) {
|
|
377
|
+
return {
|
|
378
|
+
scope: 'global',
|
|
379
|
+
kind: 'marketplace',
|
|
380
|
+
path: marketplacePath,
|
|
381
|
+
exists: true,
|
|
382
|
+
status: 'repairable',
|
|
383
|
+
issues: ['codex-marketplace-entry-missing'],
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const source = asRecord(entry.source);
|
|
388
|
+
const sourcePath = typeof source?.path === 'string' ? source.path : '';
|
|
389
|
+
const issues = source?.source === 'local' && normalizeMarketplacePath(sourcePath) === '.codex/plugins/memorix'
|
|
390
|
+
? []
|
|
391
|
+
: ['codex-marketplace-entry-stale'];
|
|
392
|
+
return {
|
|
393
|
+
scope: 'global',
|
|
394
|
+
kind: 'marketplace',
|
|
395
|
+
path: marketplacePath,
|
|
396
|
+
exists: true,
|
|
397
|
+
status: issues.length > 0 ? 'repairable' : 'ok',
|
|
398
|
+
issues,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function inspectCodexPluginRuntime(): AgentPluginCheck {
|
|
403
|
+
const command = 'codex plugin list --marketplace personal --available --json';
|
|
404
|
+
const result = spawnSync('codex', ['plugin', 'list', '--marketplace', CODEX_PLUGIN_MARKETPLACE, '--available', '--json'], {
|
|
405
|
+
encoding: 'utf-8',
|
|
406
|
+
stdio: 'pipe',
|
|
407
|
+
shell: process.platform === 'win32',
|
|
408
|
+
});
|
|
409
|
+
if (result.error || result.status !== 0) {
|
|
410
|
+
return {
|
|
411
|
+
scope: 'global',
|
|
412
|
+
kind: 'runtime',
|
|
413
|
+
path: command,
|
|
414
|
+
exists: false,
|
|
415
|
+
status: 'skipped',
|
|
416
|
+
issues: ['codex-plugin-runtime-unavailable'],
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const runtime = parseCodexPluginList(String(result.stdout ?? ''));
|
|
421
|
+
if (!runtime) {
|
|
422
|
+
return {
|
|
423
|
+
scope: 'global',
|
|
424
|
+
kind: 'runtime',
|
|
425
|
+
path: command,
|
|
426
|
+
exists: true,
|
|
427
|
+
status: 'skipped',
|
|
428
|
+
issues: ['codex-plugin-runtime-unreadable'],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const issues: string[] = [];
|
|
433
|
+
if (!runtime.installed) issues.push('codex-plugin-not-installed');
|
|
434
|
+
else if (!runtime.enabled) issues.push('codex-plugin-disabled');
|
|
435
|
+
return {
|
|
436
|
+
scope: 'global',
|
|
437
|
+
kind: 'runtime',
|
|
438
|
+
path: command,
|
|
439
|
+
exists: true,
|
|
440
|
+
status: issues.length > 0 ? 'repairable' : 'ok',
|
|
441
|
+
issues,
|
|
442
|
+
runtime,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function inspectCodexHookTrust(): Promise<AgentPluginCheck> {
|
|
447
|
+
const configPath = codexConfigPath();
|
|
448
|
+
if (!existsSync(configPath)) {
|
|
449
|
+
return {
|
|
450
|
+
scope: 'global',
|
|
451
|
+
kind: 'hook-trust',
|
|
452
|
+
path: configPath,
|
|
453
|
+
exists: false,
|
|
454
|
+
status: 'skipped',
|
|
455
|
+
issues: ['codex-hook-trust-unavailable'],
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
let config = '';
|
|
460
|
+
try {
|
|
461
|
+
config = await readFile(configPath, 'utf-8');
|
|
462
|
+
} catch {
|
|
463
|
+
return {
|
|
464
|
+
scope: 'global',
|
|
465
|
+
kind: 'hook-trust',
|
|
466
|
+
path: configPath,
|
|
467
|
+
exists: true,
|
|
468
|
+
status: 'skipped',
|
|
469
|
+
issues: ['codex-hook-trust-unreadable'],
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const trusted = CODEX_HOOK_STATE_NAMES.filter((event) =>
|
|
474
|
+
config.includes(`[hooks.state."${CODEX_PLUGIN_ID}:hooks/hooks.json:${event}:`)
|
|
475
|
+
);
|
|
476
|
+
const issues = trusted.length === CODEX_HOOK_STATE_NAMES.length ? [] : ['codex-hook-trust-pending'];
|
|
477
|
+
return {
|
|
478
|
+
scope: 'global',
|
|
479
|
+
kind: 'hook-trust',
|
|
480
|
+
path: configPath,
|
|
481
|
+
exists: true,
|
|
482
|
+
status: issues.length > 0 ? 'repairable' : 'ok',
|
|
483
|
+
issues,
|
|
484
|
+
hookTrust: { trusted, expected: [...CODEX_HOOK_STATE_NAMES] },
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function aggregatePluginStatus(checks: AgentPluginCheck[]): AgentIntegrationStatus {
|
|
489
|
+
if (checks.length === 0) return 'skipped';
|
|
490
|
+
return worstStatus(checks.map((check) => check.status));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function aggregatePluginIssues(checks: AgentPluginCheck[]): string[] {
|
|
494
|
+
const status = aggregatePluginStatus(checks);
|
|
495
|
+
if (status === 'ok') {
|
|
496
|
+
return unique(checks.filter((check) => check.status === 'skipped').flatMap((check) => check.issues));
|
|
497
|
+
}
|
|
498
|
+
if (status === 'repairable') {
|
|
499
|
+
return unique(checks
|
|
500
|
+
.filter((check) => check.status === 'repairable' || check.status === 'missing')
|
|
501
|
+
.flatMap((check) => check.issues));
|
|
502
|
+
}
|
|
503
|
+
return unique(checks
|
|
504
|
+
.filter((check) => check.status === status)
|
|
505
|
+
.flatMap((check) => check.issues));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async function inspectPlugin(agent: AgentName, scope: AgentIntegrationScope): Promise<AgentPluginStatus> {
|
|
509
|
+
if (agent !== 'codex' || scope === 'local' || scope === 'project') {
|
|
510
|
+
return { status: 'skipped', issues: [], checks: [] };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const checks = [
|
|
514
|
+
await inspectCodexPluginBundle(),
|
|
515
|
+
await inspectCodexMarketplace(),
|
|
516
|
+
inspectCodexPluginRuntime(),
|
|
517
|
+
await inspectCodexHookTrust(),
|
|
518
|
+
];
|
|
519
|
+
return {
|
|
520
|
+
status: aggregatePluginStatus(checks),
|
|
521
|
+
issues: aggregatePluginIssues(checks),
|
|
522
|
+
checks,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
182
526
|
function looksLikeStaleMemorixCommand(server: MCPServerEntry): boolean {
|
|
183
527
|
const text = [server.command, ...(server.args ?? [])].join(' ');
|
|
184
528
|
return /[\\/]\.worktrees[\\/]/i.test(text) || /[\\/]dist[\\/]cli[\\/]index\.js/i.test(text);
|
|
@@ -492,10 +836,11 @@ export async function inspectAgentIntegrations(options: {
|
|
|
492
836
|
agent,
|
|
493
837
|
mcp: await inspectMcp(agent, projectRoot, scope),
|
|
494
838
|
guidance: await inspectGuidance(agent, projectRoot, scope),
|
|
839
|
+
plugin: await inspectPlugin(agent, scope),
|
|
495
840
|
});
|
|
496
841
|
}
|
|
497
842
|
|
|
498
|
-
const entryStatuses = entries.map((entry) => worstStatus([entry.mcp.status, entry.guidance.status]));
|
|
843
|
+
const entryStatuses = entries.map((entry) => worstStatus([entry.mcp.status, entry.guidance.status, entry.plugin.status]));
|
|
499
844
|
return {
|
|
500
845
|
projectRoot,
|
|
501
846
|
scope,
|
|
@@ -519,10 +864,11 @@ export function formatAgentIntegrationReport(report: AgentIntegrationReport): st
|
|
|
519
864
|
];
|
|
520
865
|
|
|
521
866
|
for (const entry of report.entries) {
|
|
522
|
-
const status = worstStatus([entry.mcp.status, entry.guidance.status]);
|
|
867
|
+
const status = worstStatus([entry.mcp.status, entry.guidance.status, entry.plugin.status]);
|
|
523
868
|
lines.push(`${entry.agent}: ${status}`);
|
|
524
869
|
if (entry.mcp.issues.length > 0) lines.push(` MCP: ${entry.mcp.issues.join(', ')}`);
|
|
525
870
|
if (entry.guidance.issues.length > 0) lines.push(` Guidance: ${entry.guidance.issues.join(', ')}`);
|
|
871
|
+
if (entry.plugin.issues.length > 0) lines.push(` Plugin: ${entry.plugin.issues.join(', ')}`);
|
|
526
872
|
}
|
|
527
873
|
|
|
528
874
|
lines.push('');
|
|
@@ -586,6 +932,42 @@ export async function repairAgentIntegrations(options: {
|
|
|
586
932
|
} else {
|
|
587
933
|
skipped.push(`${entry.agent}:guidance`);
|
|
588
934
|
}
|
|
935
|
+
|
|
936
|
+
if (entry.agent === 'codex' && entry.plugin.status !== 'ok' && entry.plugin.status !== 'skipped') {
|
|
937
|
+
const bundleOrMarketplaceNeedsRepair = entry.plugin.checks.some((check) =>
|
|
938
|
+
(check.kind === 'bundle' || check.kind === 'marketplace') && check.status !== 'ok'
|
|
939
|
+
);
|
|
940
|
+
const runtime = entry.plugin.checks.find((check) => check.kind === 'runtime');
|
|
941
|
+
const hookTrust = entry.plugin.checks.find((check) => check.kind === 'hook-trust');
|
|
942
|
+
const needsInstall = runtime?.runtime?.installed === false;
|
|
943
|
+
const needsManualEnable = runtime?.runtime?.installed === true && runtime.runtime.enabled === false;
|
|
944
|
+
const needsHookTrust = hookTrust?.status === 'repairable';
|
|
945
|
+
const missingPluginFiles = entry.plugin.checks.some((check) =>
|
|
946
|
+
(check.kind === 'bundle' || check.kind === 'marketplace') && check.status === 'missing'
|
|
947
|
+
);
|
|
948
|
+
|
|
949
|
+
if (needsManualEnable) {
|
|
950
|
+
skipped.push('codex:plugin:global:enable-in-plugin-browser');
|
|
951
|
+
}
|
|
952
|
+
if (needsHookTrust) {
|
|
953
|
+
skipped.push('codex:plugin:global:review-hooks');
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (bundleOrMarketplaceNeedsRepair || needsInstall) {
|
|
957
|
+
if ((missingPluginFiles || needsInstall) && !canInstallMissing) {
|
|
958
|
+
skipped.push('codex:plugin:global:missing');
|
|
959
|
+
} else {
|
|
960
|
+
if (!options.dry) {
|
|
961
|
+
await installPluginPackage({ agent: 'codex' });
|
|
962
|
+
if (needsInstall) {
|
|
963
|
+
const install = tryInstallCodexPlugin();
|
|
964
|
+
if (!install.ok) skipped.push('codex:plugin:global:install-pending');
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
changed.push('codex:plugin:global');
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
589
971
|
}
|
|
590
972
|
|
|
591
973
|
return {
|
|
@@ -300,6 +300,17 @@ async function stripHookCaptureFromPlugin(pluginPath: string, agent: PluginInsta
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
async function stampCodexPluginVersion(pluginPath: string): Promise<void> {
|
|
304
|
+
const manifestPath = path.join(pluginPath, '.codex-plugin', 'plugin.json');
|
|
305
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf-8')) as Record<string, unknown>;
|
|
306
|
+
if (manifest.name !== 'memorix') {
|
|
307
|
+
throw new Error(`Unexpected Codex plugin manifest at ${manifestPath}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
manifest.version = getCliVersion();
|
|
311
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
|
|
312
|
+
}
|
|
313
|
+
|
|
303
314
|
async function stripHookCaptureFromOpenClawBundle(bundlePath: string): Promise<void> {
|
|
304
315
|
await rm(path.join(bundlePath, 'hooks'), { recursive: true, force: true });
|
|
305
316
|
|
|
@@ -636,6 +647,7 @@ export async function installPluginPackage(options: PluginInstallOptions): Promi
|
|
|
636
647
|
const pluginPath = path.join(home, '.codex', 'plugins', 'memorix');
|
|
637
648
|
const marketplacePath = path.join(home, '.agents', 'plugins', 'marketplace.json');
|
|
638
649
|
await copyDir(source, pluginPath);
|
|
650
|
+
await stampCodexPluginVersion(pluginPath);
|
|
639
651
|
if (!includeHooks) await stripHookCaptureFromPlugin(pluginPath, 'codex');
|
|
640
652
|
await writeOfficialSkills(path.join(pluginPath, 'skills'));
|
|
641
653
|
await upsertCodexMarketplace(marketplacePath, pluginPath);
|
|
@@ -862,7 +874,7 @@ function tryInstallClaudePlugin(marketplaceRoot: string): { ok: boolean; message
|
|
|
862
874
|
};
|
|
863
875
|
}
|
|
864
876
|
|
|
865
|
-
function tryInstallCodexPlugin(): { ok: boolean; message: string } {
|
|
877
|
+
export function tryInstallCodexPlugin(): { ok: boolean; message: string } {
|
|
866
878
|
const result = spawnSync('codex', ['plugin', 'add', 'memorix@personal', '--json'], {
|
|
867
879
|
encoding: 'utf-8',
|
|
868
880
|
stdio: 'pipe',
|