@ebowwa/claude-code-config-mcp 1.0.0

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/src/types.js ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigFileType = exports.HookFileSchema = exports.ClaudeMdSchema = exports.SettingsConfigSchema = exports.KeybindingsConfigSchema = exports.KeybindingSchema = void 0;
4
+ var zod_1 = require("zod");
5
+ // ============================================================================
6
+ // Claude Code Configuration Types & Schemas
7
+ // ============================================================================
8
+ /**
9
+ * Keybinding schema
10
+ */
11
+ exports.KeybindingSchema = zod_1.z.object({
12
+ key: zod_1.z.string().describe('Keyboard shortcut (e.g., "ctrl+s", "cmd+k")'),
13
+ command: zod_1.z.string().describe('Command to execute'),
14
+ when: zod_1.z.string().optional().describe('Context condition'),
15
+ });
16
+ /**
17
+ * Keybindings configuration schema
18
+ */
19
+ exports.KeybindingsConfigSchema = zod_1.z.object({
20
+ $schema: zod_1.z.string().optional(),
21
+ keybindings: zod_1.z.record(zod_1.z.string(), exports.KeybindingSchema),
22
+ });
23
+ /**
24
+ * Settings configuration schema (generic structure)
25
+ */
26
+ exports.SettingsConfigSchema = zod_1.z.object({
27
+ $schema: zod_1.z.string().optional(),
28
+ // Additional settings fields will be validated based on actual Claude Code settings structure
29
+ });
30
+ /**
31
+ * CLAUDE.md content schema
32
+ */
33
+ exports.ClaudeMdSchema = zod_1.z.object({
34
+ content: zod_1.z.string().min(1),
35
+ frontmatter: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
36
+ });
37
+ exports.HookFileSchema = zod_1.z.object({
38
+ name: zod_1.z.string(),
39
+ event: zod_1.z.string(),
40
+ enabled: zod_1.z.boolean(),
41
+ content: zod_1.z.string(),
42
+ });
43
+ /**
44
+ * Config file types
45
+ */
46
+ var ConfigFileType;
47
+ (function (ConfigFileType) {
48
+ ConfigFileType["CLAUDE_MD_GLOBAL"] = "CLAUDE_MD_GLOBAL";
49
+ ConfigFileType["CLAUDE_MD_PROJECT"] = "CLAUDE_MD_PROJECT";
50
+ ConfigFileType["KEYBINDINGS"] = "KEYBINDINGS";
51
+ ConfigFileType["SETTINGS"] = "SETTINGS";
52
+ ConfigFileType["HOOKS"] = "HOOKS";
53
+ })(ConfigFileType || (exports.ConfigFileType = ConfigFileType = {}));
package/src/types.ts ADDED
@@ -0,0 +1,237 @@
1
+ import { z } from 'zod';
2
+
3
+ // ============================================================================
4
+ // Claude Code Configuration Types & Schemas
5
+ // ============================================================================
6
+
7
+ /**
8
+ * Keybinding schema
9
+ */
10
+ export const KeybindingSchema = z.object({
11
+ key: z.string().describe('Keyboard shortcut (e.g., "ctrl+s", "cmd+k")'),
12
+ command: z.string().describe('Command to execute'),
13
+ when: z.string().optional().describe('Context condition'),
14
+ });
15
+
16
+ export type Keybinding = z.infer<typeof KeybindingSchema>;
17
+
18
+ /**
19
+ * Keybindings configuration schema
20
+ */
21
+ export const KeybindingsConfigSchema = z.object({
22
+ $schema: z.string().optional(),
23
+ keybindings: z.record(z.string(), KeybindingSchema),
24
+ });
25
+
26
+ export type KeybindingsConfig = z.infer<typeof KeybindingsConfigSchema>;
27
+
28
+ /**
29
+ * Settings configuration schema (generic structure)
30
+ */
31
+ export const SettingsConfigSchema = z.object({
32
+ $schema: z.string().optional(),
33
+ // Additional settings fields will be validated based on actual Claude Code settings structure
34
+ });
35
+
36
+ export type SettingsConfig = z.infer<typeof SettingsConfigSchema>;
37
+
38
+ /**
39
+ * CLAUDE.md content schema
40
+ */
41
+ export const ClaudeMdSchema = z.object({
42
+ content: z.string().min(1),
43
+ frontmatter: z.record(z.string(), z.string()).optional(),
44
+ });
45
+
46
+ export type ClaudeMd = z.infer<typeof ClaudeMdSchema>;
47
+
48
+ /**
49
+ * Hook file types
50
+ */
51
+ export type HookEvent =
52
+ | 'SessionStart'
53
+ | 'SessionEnd'
54
+ | 'UserPromptSubmit'
55
+ | 'PreToolUse'
56
+ | 'PostToolUse'
57
+ | 'PreCompact'
58
+ | 'Stop'
59
+ | 'SubagentStop'
60
+ | 'Notification';
61
+
62
+ export const HookFileSchema = z.object({
63
+ name: z.string(),
64
+ event: z.string(),
65
+ enabled: z.boolean(),
66
+ content: z.string(),
67
+ });
68
+
69
+ export type HookFile = z.infer<typeof HookFileSchema>;
70
+
71
+ /**
72
+ * File operation results
73
+ */
74
+ export interface FileReadResult {
75
+ success: true;
76
+ data: string;
77
+ path: string;
78
+ size: number;
79
+ modified: Date;
80
+ }
81
+
82
+ export interface FileWriteResult {
83
+ success: true;
84
+ path: string;
85
+ backup?: string;
86
+ }
87
+
88
+ export interface FileErrorResult {
89
+ success: false;
90
+ error: {
91
+ code: string;
92
+ message: string;
93
+ category: 'TRANSIENT' | 'PERMISSION' | 'VALIDATION' | 'SYSTEM' | 'UNKNOWN';
94
+ retryable: boolean;
95
+ userAction?: string;
96
+ };
97
+ }
98
+
99
+ export type FileOperationResult = FileReadResult | FileWriteResult | FileErrorResult;
100
+
101
+ /**
102
+ * Config file types
103
+ */
104
+ export enum ConfigFileType {
105
+ CLAUDE_MD_GLOBAL = 'CLAUDE_MD_GLOBAL',
106
+ CLAUDE_MD_PROJECT = 'CLAUDE_MD_PROJECT',
107
+ KEYBINDINGS = 'KEYBINDINGS',
108
+ SETTINGS = 'SETTINGS',
109
+ HOOKS = 'HOOKS',
110
+ }
111
+
112
+ /**
113
+ * Config file metadata
114
+ */
115
+ export interface ConfigFileMeta {
116
+ type: ConfigFileType;
117
+ path: string;
118
+ exists: boolean;
119
+ size?: number;
120
+ modified?: Date;
121
+ }
122
+
123
+ /**
124
+ * Atomic write options
125
+ */
126
+ export interface AtomicWriteOptions {
127
+ createBackup?: boolean;
128
+ backupDir?: string;
129
+ encoding?: BufferEncoding;
130
+ }
131
+
132
+ // ============================================================================
133
+ // MCP Server Configuration Types
134
+ // ============================================================================
135
+
136
+ /**
137
+ * MCP transport types
138
+ */
139
+ export type MCPTransport = 'stdio' | 'sse' | 'http' | 'ws' | 'websocket';
140
+
141
+ /**
142
+ * Environment variable for MCP server
143
+ */
144
+ export interface MCPEnvVar {
145
+ name: string;
146
+ value: string;
147
+ }
148
+
149
+ /**
150
+ * MCP Server configuration
151
+ */
152
+ export interface MCPServerConfig {
153
+ /** Command to run (for stdio transport) */
154
+ command?: string;
155
+ /** Arguments for the command */
156
+ args?: string[];
157
+ /** URL for HTTP/SSE/WebSocket transport */
158
+ url?: string;
159
+ /** Transport type */
160
+ transport?: MCPTransport;
161
+ /** Environment variables */
162
+ env?: Record<string, string> | MCPEnvVar[];
163
+ /** Headers for HTTP transport */
164
+ headers?: Record<string, string>;
165
+ /** Human-readable description */
166
+ description?: string;
167
+ /** Whether the server is always enabled (bypasses project-scoped approval) */
168
+ alwaysAllow?: boolean;
169
+ /** Timeout in milliseconds */
170
+ timeout?: number;
171
+ }
172
+
173
+ /**
174
+ * MCP Servers configuration (from ~/.config/claude-code/config.json)
175
+ */
176
+ export interface MCPServersConfig {
177
+ mcpServers: Record<string, MCPServerConfig>;
178
+ }
179
+
180
+ /**
181
+ * MCP Server with metadata
182
+ */
183
+ export interface MCPServerWithMeta extends MCPServerConfig {
184
+ name: string;
185
+ health?: 'connected' | 'disconnected' | 'error';
186
+ error?: string;
187
+ }
188
+
189
+ /**
190
+ * MCP add options
191
+ */
192
+ export interface MCPAddOptions {
193
+ name: string;
194
+ command?: string;
195
+ args?: string[];
196
+ url?: string;
197
+ transport?: MCPTransport;
198
+ env?: Record<string, string> | MCPEnvVar[];
199
+ headers?: Record<string, string>;
200
+ description?: string;
201
+ alwaysAllow?: boolean;
202
+ timeout?: number;
203
+ }
204
+
205
+ /**
206
+ * MCP install source types
207
+ */
208
+ export type MCPInstallSource = 'npm' | 'github' | 'local' | 'url';
209
+
210
+ /**
211
+ * MCP config target (which config file to modify)
212
+ */
213
+ export type MCPTarget = 'cli' | 'app' | 'both';
214
+
215
+ /**
216
+ * MCP install options
217
+ */
218
+ export interface MCPInstallOptions {
219
+ /** Source type (auto-detected if not specified) */
220
+ source?: MCPInstallSource;
221
+ /** Package name (npm) or repo URL (github) or local path */
222
+ package: string;
223
+ /** Name for the MCP server (defaults to package name) */
224
+ name?: string;
225
+ /** Version to install (for npm) */
226
+ version?: string;
227
+ /** Environment variables for the server */
228
+ env?: Record<string, string>;
229
+ /** Description for the server */
230
+ description?: string;
231
+ /** Arguments to pass to the server */
232
+ args?: string[];
233
+ /** Global packages directory (defaults to ~/.claude/mcp-servers) */
234
+ installDir?: string;
235
+ /** Force reinstallation */
236
+ force?: boolean;
237
+ }
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ // ============================================================================
3
+ // Error Handling - Categorization, retry logic, and user-friendly messages
4
+ // ============================================================================
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ var __generator = (this && this.__generator) || function (thisArg, body) {
15
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
16
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
17
+ function verb(n) { return function (v) { return step([n, v]); }; }
18
+ function step(op) {
19
+ if (f) throw new TypeError("Generator is already executing.");
20
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
21
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
22
+ if (y = 0, t) op = [op[0] & 2, t.value];
23
+ switch (op[0]) {
24
+ case 0: case 1: t = op; break;
25
+ case 4: _.label++; return { value: op[1], done: false };
26
+ case 5: _.label++; y = op[1]; op = [0]; continue;
27
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
28
+ default:
29
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
30
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
31
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
32
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
33
+ if (t[2]) _.ops.pop();
34
+ _.trys.pop(); continue;
35
+ }
36
+ op = body.call(thisArg, _);
37
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
38
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
39
+ }
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.ErrorCode = exports.ErrorCategory = void 0;
43
+ exports.categorizeError = categorizeError;
44
+ exports.getUserAction = getUserAction;
45
+ exports.isRetryable = isRetryable;
46
+ exports.retryWithBackoff = retryWithBackoff;
47
+ exports.createMCPError = createMCPError;
48
+ exports.getErrorCode = getErrorCode;
49
+ var ErrorCategory;
50
+ (function (ErrorCategory) {
51
+ ErrorCategory["TRANSIENT"] = "TRANSIENT";
52
+ ErrorCategory["PERMISSION"] = "PERMISSION";
53
+ ErrorCategory["VALIDATION"] = "VALIDATION";
54
+ ErrorCategory["SYSTEM"] = "SYSTEM";
55
+ ErrorCategory["UNKNOWN"] = "UNKNOWN";
56
+ })(ErrorCategory || (exports.ErrorCategory = ErrorCategory = {}));
57
+ /**
58
+ * Categorize an error for appropriate handling
59
+ */
60
+ function categorizeError(error) {
61
+ if (!(error instanceof Error)) {
62
+ return ErrorCategory.UNKNOWN;
63
+ }
64
+ var message = error.message.toLowerCase();
65
+ // Permission errors
66
+ if (message.includes('eacces') ||
67
+ message.includes('eperm') ||
68
+ message.includes('permission denied')) {
69
+ return ErrorCategory.PERMISSION;
70
+ }
71
+ // File not found (validation)
72
+ if (message.includes('enoent') || message.includes('no such file')) {
73
+ return ErrorCategory.VALIDATION;
74
+ }
75
+ // Transient errors (retryable)
76
+ if (message.includes('eagain') ||
77
+ message.includes('etimedout') ||
78
+ message.includes('econnreset') ||
79
+ message.includes('econnrefused') ||
80
+ message.includes('timeout') ||
81
+ message.includes('temporary failure')) {
82
+ return ErrorCategory.TRANSIENT;
83
+ }
84
+ // System errors
85
+ if (message.includes('enospc') ||
86
+ message.includes('disk full') ||
87
+ message.includes('eisdir') ||
88
+ message.includes('enotdir')) {
89
+ return ErrorCategory.SYSTEM;
90
+ }
91
+ return ErrorCategory.UNKNOWN;
92
+ }
93
+ /**
94
+ * Get user-friendly action message for an error
95
+ */
96
+ function getUserAction(category) {
97
+ switch (category) {
98
+ case ErrorCategory.PERMISSION:
99
+ return 'Check file permissions and ensure Claude Code has access to the config directory';
100
+ case ErrorCategory.VALIDATION:
101
+ return 'Verify the file path and try again';
102
+ case ErrorCategory.SYSTEM:
103
+ return 'Check system resources and Claude Code configuration';
104
+ case ErrorCategory.TRANSIENT:
105
+ return 'The operation failed temporarily. Please try again';
106
+ default:
107
+ return null;
108
+ }
109
+ }
110
+ /**
111
+ * Check if an error is retryable
112
+ */
113
+ function isRetryable(error) {
114
+ return categorizeError(error) === ErrorCategory.TRANSIENT;
115
+ }
116
+ var DEFAULT_RETRY_OPTIONS = {
117
+ maxAttempts: 3,
118
+ baseDelay: 1000,
119
+ maxDelay: 10000,
120
+ jitter: true,
121
+ };
122
+ /**
123
+ * Retry an operation with exponential backoff
124
+ */
125
+ function retryWithBackoff(operation_1) {
126
+ return __awaiter(this, arguments, void 0, function (operation, options) {
127
+ var lastError, _loop_1, attempt, state_1;
128
+ if (options === void 0) { options = DEFAULT_RETRY_OPTIONS; }
129
+ return __generator(this, function (_a) {
130
+ switch (_a.label) {
131
+ case 0:
132
+ _loop_1 = function (attempt) {
133
+ var _b, error_1, delay, finalDelay_1;
134
+ return __generator(this, function (_c) {
135
+ switch (_c.label) {
136
+ case 0:
137
+ _c.trys.push([0, 2, , 4]);
138
+ _b = {};
139
+ return [4 /*yield*/, operation()];
140
+ case 1: return [2 /*return*/, (_b.value = _c.sent(), _b)];
141
+ case 2:
142
+ error_1 = _c.sent();
143
+ lastError = error_1;
144
+ // Don't retry if not retryable
145
+ if (!isRetryable(error_1)) {
146
+ throw error_1;
147
+ }
148
+ delay = Math.min(options.baseDelay * Math.pow(2, attempt), options.maxDelay);
149
+ finalDelay_1 = options.jitter
150
+ ? delay + Math.random() * 1000
151
+ : delay;
152
+ // Wait before retry
153
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, finalDelay_1); })];
154
+ case 3:
155
+ // Wait before retry
156
+ _c.sent();
157
+ return [3 /*break*/, 4];
158
+ case 4: return [2 /*return*/];
159
+ }
160
+ });
161
+ };
162
+ attempt = 0;
163
+ _a.label = 1;
164
+ case 1:
165
+ if (!(attempt < options.maxAttempts)) return [3 /*break*/, 4];
166
+ return [5 /*yield**/, _loop_1(attempt)];
167
+ case 2:
168
+ state_1 = _a.sent();
169
+ if (typeof state_1 === "object")
170
+ return [2 /*return*/, state_1.value];
171
+ _a.label = 3;
172
+ case 3:
173
+ attempt++;
174
+ return [3 /*break*/, 1];
175
+ case 4: throw lastError;
176
+ }
177
+ });
178
+ });
179
+ }
180
+ function createMCPError(error) {
181
+ var category = categorizeError(error);
182
+ var message = error instanceof Error ? error.message : 'Unknown error';
183
+ return {
184
+ success: false,
185
+ error: {
186
+ category: category,
187
+ message: message,
188
+ retryable: category === ErrorCategory.TRANSIENT,
189
+ userAction: getUserAction(category),
190
+ },
191
+ };
192
+ }
193
+ /**
194
+ * Error codes for different scenarios
195
+ */
196
+ var ErrorCode;
197
+ (function (ErrorCode) {
198
+ ErrorCode["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
199
+ ErrorCode["PERMISSION_DENIED"] = "PERMISSION_DENIED";
200
+ ErrorCode["INVALID_JSON"] = "INVALID_JSON";
201
+ ErrorCode["VALIDATION_FAILED"] = "VALIDATION_FAILED";
202
+ ErrorCode["PATH_TRAVERSAL"] = "PATH_TRAVERSAL";
203
+ ErrorCode["DISK_FULL"] = "DISK_FULL";
204
+ ErrorCode["LOCKED"] = "LOCKED";
205
+ ErrorCode["UNKNOWN"] = "UNKNOWN";
206
+ })(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
207
+ /**
208
+ * Get error code from error
209
+ */
210
+ function getErrorCode(error) {
211
+ var category = categorizeError(error);
212
+ switch (category) {
213
+ case ErrorCategory.VALIDATION:
214
+ if (error instanceof Error && error.message.includes('JSON')) {
215
+ return ErrorCode.INVALID_JSON;
216
+ }
217
+ return ErrorCode.FILE_NOT_FOUND;
218
+ case ErrorCategory.PERMISSION:
219
+ return ErrorCode.PERMISSION_DENIED;
220
+ case ErrorCategory.SYSTEM:
221
+ if (error instanceof Error && error.message.includes('enospc')) {
222
+ return ErrorCode.DISK_FULL;
223
+ }
224
+ if (error instanceof Error && error.message.includes('etxtbsy')) {
225
+ return ErrorCode.LOCKED;
226
+ }
227
+ return ErrorCode.UNKNOWN;
228
+ default:
229
+ return ErrorCode.UNKNOWN;
230
+ }
231
+ }