@everstateai/mcp 1.3.2 → 1.3.3
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/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/dist/setup/auto-update.d.ts +20 -0
- package/dist/setup/auto-update.d.ts.map +1 -0
- package/dist/setup/auto-update.js +295 -0
- package/dist/setup/auto-update.js.map +1 -0
- package/dist/setup/commands/doctor.d.ts +15 -0
- package/dist/setup/commands/doctor.d.ts.map +1 -0
- package/dist/setup/commands/doctor.js +264 -0
- package/dist/setup/commands/doctor.js.map +1 -0
- package/dist/setup/commands/repair.d.ts +14 -0
- package/dist/setup/commands/repair.d.ts.map +1 -0
- package/dist/setup/commands/repair.js +252 -0
- package/dist/setup/commands/repair.js.map +1 -0
- package/dist/setup/hooks/templates.d.ts +30 -0
- package/dist/setup/hooks/templates.d.ts.map +1 -0
- package/dist/setup/hooks/templates.js +237 -0
- package/dist/setup/hooks/templates.js.map +1 -0
- package/dist/setup/types.d.ts +120 -0
- package/dist/setup/types.d.ts.map +1 -0
- package/dist/setup/types.js +58 -0
- package/dist/setup/types.js.map +1 -0
- package/dist/setup/validators/api-key.d.ts +8 -0
- package/dist/setup/validators/api-key.d.ts.map +1 -0
- package/dist/setup/validators/api-key.js +233 -0
- package/dist/setup/validators/api-key.js.map +1 -0
- package/dist/setup/validators/connectivity.d.ts +8 -0
- package/dist/setup/validators/connectivity.d.ts.map +1 -0
- package/dist/setup/validators/connectivity.js +150 -0
- package/dist/setup/validators/connectivity.js.map +1 -0
- package/dist/setup/validators/hooks.d.ts +8 -0
- package/dist/setup/validators/hooks.d.ts.map +1 -0
- package/dist/setup/validators/hooks.js +431 -0
- package/dist/setup/validators/hooks.js.map +1 -0
- package/dist/setup/validators/index.d.ts +18 -0
- package/dist/setup/validators/index.d.ts.map +1 -0
- package/dist/setup/validators/index.js +123 -0
- package/dist/setup/validators/index.js.map +1 -0
- package/dist/setup/validators/mcp-config.d.ts +8 -0
- package/dist/setup/validators/mcp-config.d.ts.map +1 -0
- package/dist/setup/validators/mcp-config.js +333 -0
- package/dist/setup/validators/mcp-config.js.map +1 -0
- package/dist/setup/validators/project.d.ts +8 -0
- package/dist/setup/validators/project.d.ts.map +1 -0
- package/dist/setup/validators/project.js +202 -0
- package/dist/setup/validators/project.js.map +1 -0
- package/dist/setup/version.d.ts +58 -0
- package/dist/setup/version.d.ts.map +1 -0
- package/dist/setup/version.js +262 -0
- package/dist/setup/version.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +207 -27
- package/dist/setup.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +32 -5
- package/src/setup/auto-update.ts +328 -0
- package/src/setup/commands/doctor.ts +266 -0
- package/src/setup/commands/repair.ts +260 -0
- package/src/setup/hooks/templates.ts +239 -0
- package/src/setup/types.ts +199 -0
- package/src/setup/validators/api-key.ts +218 -0
- package/src/setup/validators/connectivity.ts +176 -0
- package/src/setup/validators/hooks.ts +447 -0
- package/src/setup/validators/index.ts +137 -0
- package/src/setup/validators/mcp-config.ts +329 -0
- package/src/setup/validators/project.ts +179 -0
- package/src/setup/version.ts +267 -0
- package/src/setup.ts +229 -27
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repair Command
|
|
3
|
+
*
|
|
4
|
+
* Automatically fixes detected Everstate issues.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx @everstateai/mcp repair
|
|
8
|
+
* npx @everstateai/mcp repair --include-warnings
|
|
9
|
+
* npx @everstateai/mcp repair --force
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import {
|
|
14
|
+
ValidationContext,
|
|
15
|
+
ValidationResult,
|
|
16
|
+
RepairOptions,
|
|
17
|
+
c,
|
|
18
|
+
getEverstateDir,
|
|
19
|
+
} from '../types.js';
|
|
20
|
+
import { validateAll, repairResult } from '../validators/index.js';
|
|
21
|
+
import { updateVersionFile, createVersionFile, getMcpProxyVersion, HOOK_VERSIONS } from '../version.js';
|
|
22
|
+
|
|
23
|
+
interface RepairSummary {
|
|
24
|
+
attempted: number;
|
|
25
|
+
succeeded: number;
|
|
26
|
+
failed: number;
|
|
27
|
+
skipped: number;
|
|
28
|
+
details: Array<{
|
|
29
|
+
check: string;
|
|
30
|
+
result: 'fixed' | 'failed' | 'skipped' | 'manual';
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function repair(options: RepairOptions = {}): Promise<void> {
|
|
35
|
+
const context = buildContext(options);
|
|
36
|
+
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(c('bold', 'Everstate Repair'));
|
|
39
|
+
console.log(c('dim', '─'.repeat(40)));
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
// Run validation first
|
|
43
|
+
console.log('Checking installation...');
|
|
44
|
+
const summary = await validateAll(context);
|
|
45
|
+
|
|
46
|
+
// Find issues to repair
|
|
47
|
+
const toRepair = summary.results.filter((r) => {
|
|
48
|
+
if (r.status === 'fail') return true;
|
|
49
|
+
if (r.status === 'warn' && options.includeWarnings) return true;
|
|
50
|
+
return false;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (toRepair.length === 0) {
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(c('green', '✓ No issues to repair'));
|
|
56
|
+
console.log('');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(`Found ${toRepair.length} issue(s) to repair:`);
|
|
62
|
+
for (const issue of toRepair) {
|
|
63
|
+
console.log(` ${c('yellow', '•')} ${issue.component}: ${issue.check}`);
|
|
64
|
+
}
|
|
65
|
+
console.log('');
|
|
66
|
+
|
|
67
|
+
// Perform repairs
|
|
68
|
+
const repairSummary = await performRepairs(toRepair, context, options);
|
|
69
|
+
|
|
70
|
+
// Update version file on success
|
|
71
|
+
if (repairSummary.succeeded > 0) {
|
|
72
|
+
updateVersionFile({
|
|
73
|
+
components: {
|
|
74
|
+
mcpProxy: getMcpProxyVersion(),
|
|
75
|
+
hooks: HOOK_VERSIONS,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Output summary
|
|
81
|
+
outputRepairSummary(repairSummary);
|
|
82
|
+
|
|
83
|
+
// Exit code
|
|
84
|
+
if (repairSummary.failed > 0) {
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildContext(options: RepairOptions): ValidationContext {
|
|
90
|
+
const context: ValidationContext = {
|
|
91
|
+
projectDir: options.projectDir || process.cwd(),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Try to load API key
|
|
95
|
+
const apiKeyPath = `${getEverstateDir()}/api-key`;
|
|
96
|
+
if (fs.existsSync(apiKeyPath)) {
|
|
97
|
+
try {
|
|
98
|
+
context.apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
|
|
99
|
+
} catch {
|
|
100
|
+
// Will be handled during repair
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return context;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function performRepairs(
|
|
108
|
+
issues: ValidationResult[],
|
|
109
|
+
context: ValidationContext,
|
|
110
|
+
options: RepairOptions
|
|
111
|
+
): Promise<RepairSummary> {
|
|
112
|
+
const summary: RepairSummary = {
|
|
113
|
+
attempted: 0,
|
|
114
|
+
succeeded: 0,
|
|
115
|
+
failed: 0,
|
|
116
|
+
skipped: 0,
|
|
117
|
+
details: [],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const issue of issues) {
|
|
121
|
+
const checkName = `${issue.component}: ${issue.check}`;
|
|
122
|
+
|
|
123
|
+
// Check if repair is available
|
|
124
|
+
if (!issue.repairAction) {
|
|
125
|
+
console.log(` ${c('dim', '○')} ${checkName}: ${c('dim', 'No automatic repair available')}`);
|
|
126
|
+
summary.skipped++;
|
|
127
|
+
summary.details.push({ check: checkName, result: 'manual' });
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check if repair requires confirmation
|
|
132
|
+
if (!issue.repairAction.automatic && !options.force) {
|
|
133
|
+
console.log(` ${c('yellow', '?')} ${checkName}: Requires --force or manual intervention`);
|
|
134
|
+
summary.skipped++;
|
|
135
|
+
summary.details.push({ check: checkName, result: 'skipped' });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Attempt repair
|
|
140
|
+
summary.attempted++;
|
|
141
|
+
console.log(` ${c('cyan', '→')} Repairing: ${checkName}...`);
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const success = await repairResult(issue, context);
|
|
145
|
+
|
|
146
|
+
if (success) {
|
|
147
|
+
console.log(` ${c('green', '✓')} Fixed`);
|
|
148
|
+
summary.succeeded++;
|
|
149
|
+
summary.details.push({ check: checkName, result: 'fixed' });
|
|
150
|
+
} else {
|
|
151
|
+
console.log(` ${c('red', '✗')} Failed`);
|
|
152
|
+
summary.failed++;
|
|
153
|
+
summary.details.push({ check: checkName, result: 'failed' });
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.log(` ${c('red', '✗')} Error: ${String(error)}`);
|
|
157
|
+
summary.failed++;
|
|
158
|
+
summary.details.push({ check: checkName, result: 'failed' });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return summary;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function outputRepairSummary(summary: RepairSummary): void {
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(c('bold', 'Repair Summary'));
|
|
168
|
+
console.log(c('dim', '─'.repeat(40)));
|
|
169
|
+
|
|
170
|
+
if (summary.succeeded > 0) {
|
|
171
|
+
console.log(` ${c('green', '✓')} ${summary.succeeded} fixed`);
|
|
172
|
+
}
|
|
173
|
+
if (summary.failed > 0) {
|
|
174
|
+
console.log(` ${c('red', '✗')} ${summary.failed} failed`);
|
|
175
|
+
}
|
|
176
|
+
if (summary.skipped > 0) {
|
|
177
|
+
console.log(` ${c('yellow', '○')} ${summary.skipped} skipped`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log('');
|
|
181
|
+
|
|
182
|
+
if (summary.failed > 0) {
|
|
183
|
+
console.log(c('yellow', 'Some repairs failed. You may need to:'));
|
|
184
|
+
console.log(c('dim', ' 1. Run with --force for non-automatic repairs'));
|
|
185
|
+
console.log(c('dim', ' 2. Manually fix issues that cannot be auto-repaired'));
|
|
186
|
+
console.log(c('dim', ' 3. Re-run setup: npx @everstateai/mcp setup <api-key>'));
|
|
187
|
+
console.log('');
|
|
188
|
+
} else if (summary.succeeded > 0) {
|
|
189
|
+
console.log(c('green', 'All repairs completed successfully!'));
|
|
190
|
+
console.log(c('dim', 'Run `npx @everstateai/mcp doctor` to verify.'));
|
|
191
|
+
console.log('');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// CLI entry point
|
|
196
|
+
export async function repairCli(args: string[]): Promise<void> {
|
|
197
|
+
const options: RepairOptions = {};
|
|
198
|
+
|
|
199
|
+
for (let i = 0; i < args.length; i++) {
|
|
200
|
+
const arg = args[i];
|
|
201
|
+
|
|
202
|
+
switch (arg) {
|
|
203
|
+
case '--force':
|
|
204
|
+
case '-f':
|
|
205
|
+
options.force = true;
|
|
206
|
+
break;
|
|
207
|
+
case '--include-warnings':
|
|
208
|
+
case '-w':
|
|
209
|
+
options.includeWarnings = true;
|
|
210
|
+
break;
|
|
211
|
+
case '--component':
|
|
212
|
+
case '-c':
|
|
213
|
+
options.component = args[++i];
|
|
214
|
+
break;
|
|
215
|
+
case '--project':
|
|
216
|
+
case '-p':
|
|
217
|
+
options.projectDir = args[++i];
|
|
218
|
+
break;
|
|
219
|
+
case '--help':
|
|
220
|
+
case '-h':
|
|
221
|
+
printHelp();
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await repair(options);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function printHelp(): void {
|
|
230
|
+
console.log(`
|
|
231
|
+
Everstate Repair - Fix installation issues
|
|
232
|
+
|
|
233
|
+
Usage:
|
|
234
|
+
npx @everstateai/mcp repair [options]
|
|
235
|
+
|
|
236
|
+
Options:
|
|
237
|
+
--force, -f Force non-automatic repairs
|
|
238
|
+
--include-warnings, -w Also repair warnings (not just failures)
|
|
239
|
+
--component, -c Repair only a specific component
|
|
240
|
+
--project, -p Specify project directory
|
|
241
|
+
--help, -h Show this help
|
|
242
|
+
|
|
243
|
+
What can be repaired:
|
|
244
|
+
• Missing API key file (if you provide it)
|
|
245
|
+
• Incorrect file permissions
|
|
246
|
+
• Missing or outdated hooks
|
|
247
|
+
• MCP configuration issues
|
|
248
|
+
• Hook registration in Claude config
|
|
249
|
+
|
|
250
|
+
What requires manual intervention:
|
|
251
|
+
• Invalid API key (get a new one from dashboard)
|
|
252
|
+
• Missing project configuration (run setup)
|
|
253
|
+
• Network connectivity issues
|
|
254
|
+
|
|
255
|
+
Examples:
|
|
256
|
+
npx @everstateai/mcp repair
|
|
257
|
+
npx @everstateai/mcp repair --force
|
|
258
|
+
npx @everstateai/mcp repair --include-warnings
|
|
259
|
+
`);
|
|
260
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Templates
|
|
3
|
+
*
|
|
4
|
+
* Versioned hook templates for installation and updates.
|
|
5
|
+
* Each hook includes a VERSION header for validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { HookConfig, HOOK_VERSIONS, EVERSTATE_API_URL, getEverstateDir } from '../types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the sync-todos.js hook content
|
|
12
|
+
*/
|
|
13
|
+
export function getSyncTodosHook(config?: Partial<HookConfig>): string {
|
|
14
|
+
const apiKeyPath = config?.apiKeyPath || `${getEverstateDir()}/api-key`;
|
|
15
|
+
const apiUrl = config?.apiUrl || EVERSTATE_API_URL;
|
|
16
|
+
const generatedAt = config?.generatedAt || new Date().toISOString();
|
|
17
|
+
|
|
18
|
+
return `#!/usr/bin/env node
|
|
19
|
+
/**
|
|
20
|
+
* Everstate Sync Todos Hook v${HOOK_VERSIONS.syncTodos}
|
|
21
|
+
* VERSION: ${HOOK_VERSIONS.syncTodos}
|
|
22
|
+
* GENERATED: ${generatedAt}
|
|
23
|
+
*
|
|
24
|
+
* Syncs Claude's TodoWrite tasks with Everstate.
|
|
25
|
+
* Runs after every TodoWrite tool call.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
const API_KEY_PATH = '${apiKeyPath}';
|
|
32
|
+
const API_URL = '${apiUrl}';
|
|
33
|
+
|
|
34
|
+
async function main() {
|
|
35
|
+
try {
|
|
36
|
+
// Read API key
|
|
37
|
+
if (!fs.existsSync(API_KEY_PATH)) {
|
|
38
|
+
console.error('[everstate] API key not found at', API_KEY_PATH);
|
|
39
|
+
process.exit(0); // Don't block Claude
|
|
40
|
+
}
|
|
41
|
+
const apiKey = fs.readFileSync(API_KEY_PATH, 'utf8').trim();
|
|
42
|
+
|
|
43
|
+
// Parse input from Claude
|
|
44
|
+
const input = JSON.parse(process.env.TOOL_INPUT || '{}');
|
|
45
|
+
const todos = input.todos || [];
|
|
46
|
+
|
|
47
|
+
if (todos.length === 0) {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Find project config
|
|
52
|
+
let projectId = null;
|
|
53
|
+
let cwd = process.cwd();
|
|
54
|
+
for (let i = 0; i < 10; i++) {
|
|
55
|
+
const configPath = path.join(cwd, '.everstate.json');
|
|
56
|
+
if (fs.existsSync(configPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
59
|
+
projectId = config.projectId;
|
|
60
|
+
break;
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
const parent = path.dirname(cwd);
|
|
64
|
+
if (parent === cwd) break;
|
|
65
|
+
cwd = parent;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!projectId) {
|
|
69
|
+
// No project config - skip sync
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Send to API for matching and storage
|
|
74
|
+
const response = await fetch(\`\${API_URL}/api/session/sync-todos\`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: {
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
'Authorization': \`Bearer \${apiKey}\`,
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
projectId,
|
|
82
|
+
todos,
|
|
83
|
+
sessionId: process.env.SESSION_ID || null,
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
const text = await response.text();
|
|
89
|
+
console.error('[everstate] Sync failed:', response.status, text);
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error('[everstate] Hook error:', error.message);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
main();
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get the session-start.sh hook content
|
|
104
|
+
*/
|
|
105
|
+
export function getSessionStartHook(config?: Partial<HookConfig>): string {
|
|
106
|
+
const generatedAt = config?.generatedAt || new Date().toISOString();
|
|
107
|
+
|
|
108
|
+
return `#!/bin/bash
|
|
109
|
+
# Everstate Session Start Hook v${HOOK_VERSIONS.sessionStart}
|
|
110
|
+
# VERSION: ${HOOK_VERSIONS.sessionStart}
|
|
111
|
+
# GENERATED: ${generatedAt}
|
|
112
|
+
#
|
|
113
|
+
# Loads context at session start.
|
|
114
|
+
# Place in .claude/hooks/ directory.
|
|
115
|
+
|
|
116
|
+
# Check for MCP - if available, tools handle context loading
|
|
117
|
+
if command -v claude &> /dev/null; then
|
|
118
|
+
# Claude Code detected - MCP tools will load context
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Fallback for environments without MCP
|
|
123
|
+
echo "[everstate] Session starting - use sync() to load context"
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the session-end.sh hook content
|
|
129
|
+
*/
|
|
130
|
+
export function getSessionEndHook(config?: Partial<HookConfig>): string {
|
|
131
|
+
const apiKeyPath = config?.apiKeyPath || `${getEverstateDir()}/api-key`;
|
|
132
|
+
const apiUrl = config?.apiUrl || EVERSTATE_API_URL;
|
|
133
|
+
const generatedAt = config?.generatedAt || new Date().toISOString();
|
|
134
|
+
|
|
135
|
+
return `#!/bin/bash
|
|
136
|
+
# Everstate Session End Hook v${HOOK_VERSIONS.sessionEnd}
|
|
137
|
+
# VERSION: ${HOOK_VERSIONS.sessionEnd}
|
|
138
|
+
# GENERATED: ${generatedAt}
|
|
139
|
+
#
|
|
140
|
+
# Saves session summary when Claude session ends.
|
|
141
|
+
|
|
142
|
+
API_KEY_PATH="${apiKeyPath}"
|
|
143
|
+
API_URL="${apiUrl}"
|
|
144
|
+
|
|
145
|
+
# Read API key
|
|
146
|
+
if [ ! -f "$API_KEY_PATH" ]; then
|
|
147
|
+
exit 0
|
|
148
|
+
fi
|
|
149
|
+
API_KEY=$(cat "$API_KEY_PATH")
|
|
150
|
+
|
|
151
|
+
# Find project config
|
|
152
|
+
find_project_id() {
|
|
153
|
+
local dir="$PWD"
|
|
154
|
+
for i in {1..10}; do
|
|
155
|
+
if [ -f "$dir/.everstate.json" ]; then
|
|
156
|
+
grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' "$dir/.everstate.json" | cut -d'"' -f4
|
|
157
|
+
return
|
|
158
|
+
fi
|
|
159
|
+
dir=$(dirname "$dir")
|
|
160
|
+
[ "$dir" = "/" ] && break
|
|
161
|
+
done
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
PROJECT_ID=$(find_project_id)
|
|
165
|
+
if [ -z "$PROJECT_ID" ]; then
|
|
166
|
+
exit 0
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Signal session end to API
|
|
170
|
+
curl -s -X POST "$API_URL/api/session/end" \\
|
|
171
|
+
-H "Content-Type: application/json" \\
|
|
172
|
+
-H "Authorization: Bearer $API_KEY" \\
|
|
173
|
+
-d "{\\"projectId\\": \\"$PROJECT_ID\\"}" > /dev/null 2>&1
|
|
174
|
+
|
|
175
|
+
exit 0
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get the pre-compact.sh hook content (for context compaction)
|
|
181
|
+
*/
|
|
182
|
+
export function getPreCompactHook(config?: Partial<HookConfig>): string {
|
|
183
|
+
const apiKeyPath = config?.apiKeyPath || `${getEverstateDir()}/api-key`;
|
|
184
|
+
const apiUrl = config?.apiUrl || EVERSTATE_API_URL;
|
|
185
|
+
const generatedAt = config?.generatedAt || new Date().toISOString();
|
|
186
|
+
|
|
187
|
+
return `#!/bin/bash
|
|
188
|
+
# Everstate Pre-Compact Hook v${HOOK_VERSIONS.sessionEnd}
|
|
189
|
+
# VERSION: ${HOOK_VERSIONS.sessionEnd}
|
|
190
|
+
# GENERATED: ${generatedAt}
|
|
191
|
+
#
|
|
192
|
+
# Saves session context before Claude's context compaction.
|
|
193
|
+
|
|
194
|
+
API_KEY_PATH="${apiKeyPath}"
|
|
195
|
+
API_URL="${apiUrl}"
|
|
196
|
+
|
|
197
|
+
# Read API key
|
|
198
|
+
if [ ! -f "$API_KEY_PATH" ]; then
|
|
199
|
+
exit 0
|
|
200
|
+
fi
|
|
201
|
+
API_KEY=$(cat "$API_KEY_PATH")
|
|
202
|
+
|
|
203
|
+
# Find project config
|
|
204
|
+
find_project_id() {
|
|
205
|
+
local dir="$PWD"
|
|
206
|
+
for i in {1..10}; do
|
|
207
|
+
if [ -f "$dir/.everstate.json" ]; then
|
|
208
|
+
grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' "$dir/.everstate.json" | cut -d'"' -f4
|
|
209
|
+
return
|
|
210
|
+
fi
|
|
211
|
+
dir=$(dirname "$dir")
|
|
212
|
+
[ "$dir" = "/" ] && break
|
|
213
|
+
done
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
PROJECT_ID=$(find_project_id)
|
|
217
|
+
if [ -z "$PROJECT_ID" ]; then
|
|
218
|
+
exit 0
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Get transcript from environment if available
|
|
222
|
+
TRANSCRIPT="\${CLAUDE_TRANSCRIPT:-}"
|
|
223
|
+
|
|
224
|
+
# Signal pre-compact to API
|
|
225
|
+
curl -s -X POST "$API_URL/api/session/pre-compact" \\
|
|
226
|
+
-H "Content-Type: application/json" \\
|
|
227
|
+
-H "Authorization: Bearer $API_KEY" \\
|
|
228
|
+
-d "{\\"projectId\\": \\"$PROJECT_ID\\", \\"transcript\\": \\"\$TRANSCRIPT\\"}" > /dev/null 2>&1
|
|
229
|
+
|
|
230
|
+
exit 0
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const hookTemplates = {
|
|
235
|
+
syncTodos: getSyncTodosHook,
|
|
236
|
+
sessionStart: getSessionStartHook,
|
|
237
|
+
sessionEnd: getSessionEndHook,
|
|
238
|
+
preCompact: getPreCompactHook,
|
|
239
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everstate Setup Types
|
|
3
|
+
*
|
|
4
|
+
* Shared types for the setup, doctor, and repair commands.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Validation Types
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
export type ValidationStatus = 'pass' | 'warn' | 'fail';
|
|
12
|
+
|
|
13
|
+
export type RepairType = 'install' | 'update' | 'configure' | 'delete' | 'chmod';
|
|
14
|
+
|
|
15
|
+
export interface RepairAction {
|
|
16
|
+
type: RepairType;
|
|
17
|
+
description: string;
|
|
18
|
+
automatic: boolean; // Can be auto-repaired without user input
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ValidationResult {
|
|
22
|
+
component: string;
|
|
23
|
+
check: string;
|
|
24
|
+
status: ValidationStatus;
|
|
25
|
+
message: string;
|
|
26
|
+
details?: Record<string, unknown>;
|
|
27
|
+
repairAction?: RepairAction;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Validator {
|
|
31
|
+
name: string;
|
|
32
|
+
validate(context: ValidationContext): Promise<ValidationResult[]>;
|
|
33
|
+
repair?(result: ValidationResult, context: ValidationContext): Promise<boolean>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ValidationContext {
|
|
37
|
+
projectDir?: string;
|
|
38
|
+
apiKey?: string;
|
|
39
|
+
verbose?: boolean;
|
|
40
|
+
skipNetwork?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ValidationSummary {
|
|
44
|
+
passed: number;
|
|
45
|
+
warnings: number;
|
|
46
|
+
failed: number;
|
|
47
|
+
results: ValidationResult[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Version Tracking Types
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
export interface HookVersions {
|
|
55
|
+
sessionStart: string;
|
|
56
|
+
sessionEnd: string;
|
|
57
|
+
syncTodos: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ComponentVersions {
|
|
61
|
+
mcpProxy: string;
|
|
62
|
+
hooks: HookVersions;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ConfigPaths {
|
|
66
|
+
claudeCode: string;
|
|
67
|
+
claudeDesktop?: string;
|
|
68
|
+
apiKey: string;
|
|
69
|
+
globalHooks: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ProjectInstallation {
|
|
73
|
+
projectId: string;
|
|
74
|
+
configVersion: string;
|
|
75
|
+
hooksVersion: string;
|
|
76
|
+
lastValidated: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface VersionFile {
|
|
80
|
+
installedAt: string;
|
|
81
|
+
installedBy: 'setup' | 'npx' | 'manual';
|
|
82
|
+
components: ComponentVersions;
|
|
83
|
+
configPaths: ConfigPaths;
|
|
84
|
+
projects: Record<string, ProjectInstallation>;
|
|
85
|
+
lastUpdateCheck?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Hook Types
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
export interface HookTemplate {
|
|
93
|
+
name: string;
|
|
94
|
+
version: string;
|
|
95
|
+
filename: string;
|
|
96
|
+
content: (config: HookConfig) => string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface HookConfig {
|
|
100
|
+
apiKeyPath: string;
|
|
101
|
+
apiUrl: string;
|
|
102
|
+
generatedAt: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Setup Types
|
|
107
|
+
// ============================================================================
|
|
108
|
+
|
|
109
|
+
export interface SetupOptions {
|
|
110
|
+
apiKey: string;
|
|
111
|
+
projectDir: string;
|
|
112
|
+
projectId?: string;
|
|
113
|
+
skipHooks?: boolean;
|
|
114
|
+
force?: boolean;
|
|
115
|
+
globalOnly?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface DoctorOptions {
|
|
119
|
+
projectDir?: string;
|
|
120
|
+
json?: boolean;
|
|
121
|
+
verbose?: boolean;
|
|
122
|
+
component?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface RepairOptions {
|
|
126
|
+
projectDir?: string;
|
|
127
|
+
force?: boolean;
|
|
128
|
+
component?: string;
|
|
129
|
+
includeWarnings?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// Console Output Types
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
export interface Colors {
|
|
137
|
+
reset: string;
|
|
138
|
+
red: string;
|
|
139
|
+
green: string;
|
|
140
|
+
yellow: string;
|
|
141
|
+
blue: string;
|
|
142
|
+
cyan: string;
|
|
143
|
+
bold: string;
|
|
144
|
+
dim: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const COLORS: Colors = {
|
|
148
|
+
reset: '\x1b[0m',
|
|
149
|
+
red: '\x1b[31m',
|
|
150
|
+
green: '\x1b[32m',
|
|
151
|
+
yellow: '\x1b[33m',
|
|
152
|
+
blue: '\x1b[34m',
|
|
153
|
+
cyan: '\x1b[36m',
|
|
154
|
+
bold: '\x1b[1m',
|
|
155
|
+
dim: '\x1b[2m',
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export function supportsColor(): boolean {
|
|
159
|
+
return (
|
|
160
|
+
process.stdout.isTTY === true &&
|
|
161
|
+
process.env.FORCE_COLOR !== '0' &&
|
|
162
|
+
(process.platform !== 'win32' ||
|
|
163
|
+
process.env.TERM === 'xterm-256color' ||
|
|
164
|
+
!!process.env.WT_SESSION)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function c(color: keyof Colors, text: string): string {
|
|
169
|
+
return supportsColor() ? `${COLORS[color]}${text}${COLORS.reset}` : text;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// Constants
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
export const HOOK_VERSIONS = {
|
|
177
|
+
sessionStart: '1.1.0',
|
|
178
|
+
sessionEnd: '1.1.0',
|
|
179
|
+
syncTodos: '5.0.0',
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export const EVERSTATE_API_URL = 'https://www.everstate.ai';
|
|
183
|
+
|
|
184
|
+
export function getEverstateDir(): string {
|
|
185
|
+
return `${process.env.HOME}/.everstate`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function getClaudeConfigPath(): string {
|
|
189
|
+
return `${process.env.HOME}/.claude.json`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function getClaudeDesktopConfigPath(): string {
|
|
193
|
+
if (process.platform === 'darwin') {
|
|
194
|
+
return `${process.env.HOME}/Library/Application Support/Claude/claude_desktop_config.json`;
|
|
195
|
+
} else if (process.platform === 'win32') {
|
|
196
|
+
return `${process.env.APPDATA}/Claude/claude_desktop_config.json`;
|
|
197
|
+
}
|
|
198
|
+
return ''; // Linux - no known Claude Desktop location
|
|
199
|
+
}
|