@getjack/jack 0.1.0 → 0.1.1

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.
@@ -0,0 +1,175 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { platform } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+
7
+ /**
8
+ * MCP server configuration structure
9
+ */
10
+ export interface McpServerConfig {
11
+ command: string;
12
+ args: string[];
13
+ env?: Record<string, string>;
14
+ }
15
+
16
+ /**
17
+ * IDE-specific MCP configuration paths and settings
18
+ */
19
+ interface IdeMcpConfig {
20
+ path: string;
21
+ key: string;
22
+ }
23
+
24
+ /**
25
+ * IDE MCP configuration paths
26
+ * Maps IDE ID to its config file path and MCP servers key
27
+ */
28
+ export const IDE_MCP_CONFIGS: Record<string, IdeMcpConfig> = {
29
+ "claude-code": {
30
+ path: join(homedir(), ".claude.json"),
31
+ key: "mcpServers",
32
+ },
33
+ "claude-desktop": {
34
+ path:
35
+ platform() === "darwin"
36
+ ? join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json")
37
+ : platform() === "win32"
38
+ ? join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json")
39
+ : join(homedir(), ".config", "Claude", "claude_desktop_config.json"),
40
+ key: "mcpServers",
41
+ },
42
+ };
43
+
44
+ /**
45
+ * Jack MCP configuration storage path
46
+ */
47
+ const JACK_MCP_CONFIG_DIR = join(homedir(), ".config", "jack", "mcp");
48
+ const JACK_MCP_CONFIG_PATH = join(JACK_MCP_CONFIG_DIR, "config.json");
49
+
50
+ /**
51
+ * Returns the jack MCP server configuration
52
+ */
53
+ export function getJackMcpConfig(): McpServerConfig {
54
+ return {
55
+ command: "jack",
56
+ args: ["mcp", "serve"],
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Get display name for IDE
62
+ */
63
+ export function getIdeDisplayName(ideId: string): string {
64
+ const displayNames: Record<string, string> = {
65
+ "claude-code": "Claude Code",
66
+ "claude-desktop": "Claude Desktop",
67
+ };
68
+ return displayNames[ideId] || ideId;
69
+ }
70
+
71
+ /**
72
+ * Check if an IDE's config directory exists (indicating it's installed)
73
+ */
74
+ export function isIdeInstalled(ideId: string): boolean {
75
+ const ideConfig = IDE_MCP_CONFIGS[ideId];
76
+ if (!ideConfig) return false;
77
+
78
+ // Check if the parent directory exists (config file itself may not exist yet)
79
+ const configDir = dirname(ideConfig.path);
80
+ return existsSync(configDir);
81
+ }
82
+
83
+ /**
84
+ * Install MCP config to a single IDE
85
+ * Reads existing config, merges jack server, writes back
86
+ * Returns true on success
87
+ */
88
+ export async function installMcpConfigToIde(ideId: string): Promise<boolean> {
89
+ const ideConfig = IDE_MCP_CONFIGS[ideId];
90
+ if (!ideConfig) {
91
+ throw new Error(`Unknown IDE: ${ideId}`);
92
+ }
93
+
94
+ // Ensure parent directory exists
95
+ const configDir = dirname(ideConfig.path);
96
+ if (!existsSync(configDir)) {
97
+ try {
98
+ await mkdir(configDir, { recursive: true });
99
+ } catch (error) {
100
+ // Directory doesn't exist and can't be created - IDE not installed
101
+ return false;
102
+ }
103
+ }
104
+
105
+ // Read existing config or start with empty object
106
+ let existingConfig: Record<string, unknown> = {};
107
+ if (existsSync(ideConfig.path)) {
108
+ try {
109
+ existingConfig = await Bun.file(ideConfig.path).json();
110
+ } catch {
111
+ // Invalid JSON - treat as empty config
112
+ existingConfig = {};
113
+ }
114
+ }
115
+
116
+ // Get or create mcpServers object
117
+ const mcpServers = (existingConfig[ideConfig.key] as Record<string, unknown>) || {};
118
+
119
+ // Add/update jack MCP server
120
+ mcpServers.jack = getJackMcpConfig();
121
+
122
+ // Merge back into config
123
+ existingConfig[ideConfig.key] = mcpServers;
124
+
125
+ // Write updated config
126
+ try {
127
+ await Bun.write(ideConfig.path, JSON.stringify(existingConfig, null, 2));
128
+ return true;
129
+ } catch {
130
+ // Write failed (permissions, etc.)
131
+ return false;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Install to ALL detected/installed IDEs
137
+ * Returns array of IDE IDs that were configured
138
+ */
139
+ export async function installMcpConfigsToAllIdes(): Promise<string[]> {
140
+ const configured: string[] = [];
141
+
142
+ for (const ideId of Object.keys(IDE_MCP_CONFIGS)) {
143
+ // Check if IDE is installed
144
+ if (!isIdeInstalled(ideId)) {
145
+ continue;
146
+ }
147
+
148
+ // Try to install config
149
+ const success = await installMcpConfigToIde(ideId);
150
+ if (success) {
151
+ configured.push(ideId);
152
+ }
153
+ }
154
+
155
+ return configured;
156
+ }
157
+
158
+ /**
159
+ * Save jack's MCP config for future reference
160
+ */
161
+ export async function saveMcpConfig(): Promise<void> {
162
+ // Ensure directory exists
163
+ if (!existsSync(JACK_MCP_CONFIG_DIR)) {
164
+ await mkdir(JACK_MCP_CONFIG_DIR, { recursive: true });
165
+ }
166
+
167
+ // Save the jack MCP config
168
+ const config = {
169
+ version: 1,
170
+ mcpServer: getJackMcpConfig(),
171
+ installedAt: new Date().toISOString(),
172
+ };
173
+
174
+ await Bun.write(JACK_MCP_CONFIG_PATH, JSON.stringify(config, null, 2));
175
+ }