@everstateai/mcp 1.3.12 → 1.3.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/README.md +26 -3
- package/dist/index.js +462 -12
- package/dist/index.js.map +1 -1
- package/dist/setup/auto-update.d.ts.map +1 -1
- package/dist/setup/auto-update.js +223 -0
- package/dist/setup/auto-update.js.map +1 -1
- package/dist/setup/hooks/templates.d.ts +18 -1
- package/dist/setup/hooks/templates.d.ts.map +1 -1
- package/dist/setup/hooks/templates.js +381 -41
- package/dist/setup/hooks/templates.js.map +1 -1
- package/dist/setup/types.d.ts +2 -0
- package/dist/setup/types.d.ts.map +1 -1
- package/dist/setup/types.js +3 -1
- package/dist/setup/types.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +98 -8
- package/dist/setup.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +503 -13
- package/src/setup/auto-update.ts +263 -0
- package/src/setup/hooks/templates.ts +398 -41
- package/src/setup/types.ts +3 -1
- package/src/setup.ts +113 -8
package/src/setup/auto-update.ts
CHANGED
|
@@ -95,6 +95,59 @@ export async function checkAndApplyUpdates(): Promise<UpdateCheckResult> {
|
|
|
95
95
|
result.needsUpdate = true;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// Install gotcha-check hook if not present
|
|
99
|
+
const gotchaCheckPath = `${hooksDir}/gotcha-check.js`;
|
|
100
|
+
if (!fs.existsSync(gotchaCheckPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const { getGotchaCheckHook } = await import('./hooks/templates.js');
|
|
103
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
104
|
+
fs.writeFileSync(gotchaCheckPath, getGotchaCheckHook());
|
|
105
|
+
fs.chmodSync(gotchaCheckPath, '755');
|
|
106
|
+
result.updates.push('gotcha-check.js installed');
|
|
107
|
+
result.needsUpdate = true;
|
|
108
|
+
|
|
109
|
+
// Register PreToolUse hook for Edit and Write
|
|
110
|
+
const gotchaRegistered = await ensureGotchaHookRegistration(gotchaCheckPath);
|
|
111
|
+
if (gotchaRegistered) {
|
|
112
|
+
result.updates.push('PreToolUse gotcha hook registered');
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
result.errors.push(`Failed to install gotcha-check.js: ${String(error)}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Install implicit-progress hook if not present
|
|
120
|
+
const implicitProgressPath = `${hooksDir}/implicit-progress.js`;
|
|
121
|
+
if (!fs.existsSync(implicitProgressPath)) {
|
|
122
|
+
try {
|
|
123
|
+
const { getImplicitProgressHook } = await import('./hooks/templates.js');
|
|
124
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
125
|
+
fs.writeFileSync(implicitProgressPath, getImplicitProgressHook());
|
|
126
|
+
fs.chmodSync(implicitProgressPath, '755');
|
|
127
|
+
result.updates.push('implicit-progress.js installed');
|
|
128
|
+
result.needsUpdate = true;
|
|
129
|
+
|
|
130
|
+
// Register PostToolUse hook for Edit, Write, and Bash
|
|
131
|
+
const progressRegistered = await ensureImplicitProgressRegistration(implicitProgressPath);
|
|
132
|
+
if (progressRegistered) {
|
|
133
|
+
result.updates.push('PostToolUse implicit-progress hook registered');
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
result.errors.push(`Failed to install implicit-progress.js: ${String(error)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Migrate Stop→SessionEnd and ensure PreCompact/permissions in known projects
|
|
141
|
+
try {
|
|
142
|
+
const migrateResult = await migrateProjectSettings(versionFile);
|
|
143
|
+
if (migrateResult.length > 0) {
|
|
144
|
+
result.updates.push(...migrateResult);
|
|
145
|
+
result.needsUpdate = true;
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
result.errors.push(`Project migration failed: ${String(error)}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
98
151
|
// Update MCP proxy version in version file
|
|
99
152
|
const currentVersion = getMcpProxyVersion();
|
|
100
153
|
if (versionFile.components.mcpProxy !== currentVersion) {
|
|
@@ -326,3 +379,213 @@ async function ensureHookRegistration(syncTodosPath: string): Promise<boolean> {
|
|
|
326
379
|
}
|
|
327
380
|
}
|
|
328
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Ensure PreToolUse gotcha hook is registered in ~/.claude.json for Edit and Write
|
|
384
|
+
*/
|
|
385
|
+
async function ensureGotchaHookRegistration(gotchaCheckPath: string): Promise<boolean> {
|
|
386
|
+
const configPath = getClaudeConfigPath();
|
|
387
|
+
|
|
388
|
+
if (!fs.existsSync(configPath)) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
394
|
+
|
|
395
|
+
if (!config.hooks) {
|
|
396
|
+
config.hooks = {};
|
|
397
|
+
}
|
|
398
|
+
if (!config.hooks.PreToolUse) {
|
|
399
|
+
config.hooks.PreToolUse = [];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const preToolUse = config.hooks.PreToolUse as Array<{
|
|
403
|
+
matcher: string;
|
|
404
|
+
hooks: Array<{ type?: string; command?: string; timeout?: number }>;
|
|
405
|
+
}>;
|
|
406
|
+
|
|
407
|
+
const expectedCommand = `node ${gotchaCheckPath}`;
|
|
408
|
+
let updated = false;
|
|
409
|
+
|
|
410
|
+
for (const toolName of ['Edit', 'Write']) {
|
|
411
|
+
let entry = preToolUse.find((e) => e.matcher === toolName);
|
|
412
|
+
if (!entry) {
|
|
413
|
+
entry = { matcher: toolName, hooks: [] };
|
|
414
|
+
preToolUse.push(entry);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const hasGotchaHook = entry.hooks.some((h) => h.command?.includes('gotcha-check'));
|
|
418
|
+
if (!hasGotchaHook) {
|
|
419
|
+
entry.hooks.push({
|
|
420
|
+
type: 'command',
|
|
421
|
+
command: expectedCommand,
|
|
422
|
+
timeout: 5,
|
|
423
|
+
});
|
|
424
|
+
updated = true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (updated) {
|
|
429
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
430
|
+
}
|
|
431
|
+
return updated;
|
|
432
|
+
} catch {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Ensure PostToolUse implicit-progress hook is registered for Edit, Write, Bash
|
|
439
|
+
*/
|
|
440
|
+
async function ensureImplicitProgressRegistration(hookPath: string): Promise<boolean> {
|
|
441
|
+
const configPath = getClaudeConfigPath();
|
|
442
|
+
|
|
443
|
+
if (!fs.existsSync(configPath)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
449
|
+
|
|
450
|
+
if (!config.hooks) {
|
|
451
|
+
config.hooks = {};
|
|
452
|
+
}
|
|
453
|
+
if (!config.hooks.PostToolUse) {
|
|
454
|
+
config.hooks.PostToolUse = [];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const postToolUse = config.hooks.PostToolUse as Array<{
|
|
458
|
+
matcher: string;
|
|
459
|
+
hooks: Array<{ type?: string; command?: string; timeout?: number }>;
|
|
460
|
+
}>;
|
|
461
|
+
|
|
462
|
+
const expectedCommand = `node ${hookPath}`;
|
|
463
|
+
let updated = false;
|
|
464
|
+
|
|
465
|
+
for (const toolName of ['Edit', 'Write', 'Bash']) {
|
|
466
|
+
let entry = postToolUse.find((e) => e.matcher === toolName);
|
|
467
|
+
if (!entry) {
|
|
468
|
+
entry = { matcher: toolName, hooks: [] };
|
|
469
|
+
postToolUse.push(entry);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const hasHook = entry.hooks.some((h) => h.command?.includes('implicit-progress'));
|
|
473
|
+
if (!hasHook) {
|
|
474
|
+
entry.hooks.push({
|
|
475
|
+
type: 'command',
|
|
476
|
+
command: expectedCommand,
|
|
477
|
+
timeout: 5,
|
|
478
|
+
});
|
|
479
|
+
updated = true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (updated) {
|
|
484
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
485
|
+
}
|
|
486
|
+
return updated;
|
|
487
|
+
} catch {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Migrate project-level settings in known projects:
|
|
494
|
+
* 1. Move Stop hooks → SessionEnd (bug fix from older installs)
|
|
495
|
+
* 2. Ensure PreCompact hook is registered
|
|
496
|
+
* 3. Ensure mcp__everstate__* permission is present
|
|
497
|
+
*/
|
|
498
|
+
async function migrateProjectSettings(
|
|
499
|
+
versionFile: ReturnType<typeof readVersionFile>
|
|
500
|
+
): Promise<string[]> {
|
|
501
|
+
const updates: string[] = [];
|
|
502
|
+
|
|
503
|
+
if (!versionFile?.projects) return updates;
|
|
504
|
+
|
|
505
|
+
for (const [projectId, projectInfo] of Object.entries(versionFile.projects)) {
|
|
506
|
+
const projectDir = (projectInfo as any).projectDir;
|
|
507
|
+
if (!projectDir || !fs.existsSync(projectDir)) continue;
|
|
508
|
+
|
|
509
|
+
const settingsPath = `${projectDir}/.claude/settings.local.json`;
|
|
510
|
+
if (!fs.existsSync(settingsPath)) continue;
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
514
|
+
const settings = JSON.parse(content);
|
|
515
|
+
if (!settings.hooks) continue;
|
|
516
|
+
|
|
517
|
+
let modified = false;
|
|
518
|
+
|
|
519
|
+
// 1. Migrate Stop → SessionEnd for everstate hooks
|
|
520
|
+
if (settings.hooks.Stop && Array.isArray(settings.hooks.Stop)) {
|
|
521
|
+
const everstateEntries = settings.hooks.Stop.filter((entry: any) =>
|
|
522
|
+
entry.hooks?.some((h: any) => h.command?.includes('everstate'))
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
if (everstateEntries.length > 0) {
|
|
526
|
+
settings.hooks.Stop = settings.hooks.Stop.filter((entry: any) =>
|
|
527
|
+
!entry.hooks?.some((h: any) => h.command?.includes('everstate'))
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
if (!settings.hooks.SessionEnd) settings.hooks.SessionEnd = [];
|
|
531
|
+
for (const entry of everstateEntries) {
|
|
532
|
+
for (const hook of (entry.hooks || [])) {
|
|
533
|
+
if (hook.command) {
|
|
534
|
+
hook.command = hook.command
|
|
535
|
+
.replace('session-stop.sh', 'session-end.sh')
|
|
536
|
+
.replace('everstate-session-stop', 'everstate-session-end');
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const alreadyExists = settings.hooks.SessionEnd.some((e: any) =>
|
|
540
|
+
e.hooks?.some((h: any) => h.command?.includes('everstate'))
|
|
541
|
+
);
|
|
542
|
+
if (!alreadyExists) {
|
|
543
|
+
settings.hooks.SessionEnd.push(entry);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
modified = true;
|
|
548
|
+
updates.push(`${projectId}: Migrated Stop→SessionEnd hooks`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 2. Ensure PreCompact hook is registered
|
|
553
|
+
if (!settings.hooks.PreCompact || !Array.isArray(settings.hooks.PreCompact)) {
|
|
554
|
+
settings.hooks.PreCompact = [];
|
|
555
|
+
}
|
|
556
|
+
const hasPreCompact = settings.hooks.PreCompact.some((entry: any) =>
|
|
557
|
+
entry.hooks?.some((h: any) => h.command?.includes('pre-compact'))
|
|
558
|
+
);
|
|
559
|
+
if (!hasPreCompact) {
|
|
560
|
+
const preCompactPath = `${projectDir}/.claude/hooks/everstate-pre-compact.sh`;
|
|
561
|
+
if (fs.existsSync(preCompactPath)) {
|
|
562
|
+
settings.hooks.PreCompact.push({
|
|
563
|
+
matcher: '*',
|
|
564
|
+
hooks: [{ type: 'command', command: preCompactPath }],
|
|
565
|
+
});
|
|
566
|
+
modified = true;
|
|
567
|
+
updates.push(`${projectId}: Registered PreCompact hook`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// 3. Ensure mcp__everstate__* permission
|
|
572
|
+
if (!settings.permissions) settings.permissions = {};
|
|
573
|
+
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
574
|
+
const everstatePermission = 'mcp__everstate__*';
|
|
575
|
+
if (!settings.permissions.allow.includes(everstatePermission)) {
|
|
576
|
+
settings.permissions.allow.push(everstatePermission);
|
|
577
|
+
modified = true;
|
|
578
|
+
updates.push(`${projectId}: Added mcp__everstate__* permission`);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (modified) {
|
|
582
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
583
|
+
}
|
|
584
|
+
} catch {
|
|
585
|
+
// Skip projects with invalid settings
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return updates;
|
|
590
|
+
}
|
|
591
|
+
|