ragcode-context-engine 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,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Interactive configuration wizard for RagCode
4
+ */
5
+ import { type RuntimeConfigFile } from '../src/config/runtime-config.js';
6
+ export interface InitConfigOptions {
7
+ targetDir?: string;
8
+ defaults?: boolean;
9
+ }
10
+ export interface InitConfigResult {
11
+ targetDir: string;
12
+ configPath: string;
13
+ config: RuntimeConfigFile;
14
+ }
15
+ export declare function runInitConfig(options?: InitConfigOptions): Promise<InitConfigResult>;
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Interactive configuration wizard for RagCode
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import readline from 'node:readline';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { DEFAULT_RUNTIME_CONFIG, writeRuntimeConfigFile } from '../src/config/runtime-config.js';
10
+ const DEFAULT_INIT_CONFIG = {
11
+ graphStore: DEFAULT_RUNTIME_CONFIG.graphStore,
12
+ sqlitePath: DEFAULT_RUNTIME_CONFIG.sqlitePath,
13
+ semanticStore: DEFAULT_RUNTIME_CONFIG.semanticStore,
14
+ lancedbUri: DEFAULT_RUNTIME_CONFIG.lancedbUri,
15
+ embeddingProvider: DEFAULT_RUNTIME_CONFIG.embeddingProvider
16
+ };
17
+ function createInterface() {
18
+ return readline.createInterface({
19
+ input: process.stdin,
20
+ output: process.stdout
21
+ });
22
+ }
23
+ function question(rl, prompt) {
24
+ return new Promise(resolve => {
25
+ rl.question(prompt, answer => resolve(answer.trim()));
26
+ });
27
+ }
28
+ async function runWizard() {
29
+ const rl = createInterface();
30
+ const config = {
31
+ graphStore: 'sqlite',
32
+ semanticStore: 'lancedb',
33
+ embeddingProvider: 'deterministic'
34
+ };
35
+ console.log('šŸ§™ RagCode Configuration Wizard\n');
36
+ // Graph store
37
+ console.log('šŸ“Š Graph Store (structural code graph)');
38
+ const graphStore = await question(rl, ' Choose: [1] SQLite (persistent), [2] Memory (testing only) [1]: ');
39
+ config.graphStore = graphStore === '2' ? 'memory' : 'sqlite';
40
+ if (config.graphStore === 'sqlite') {
41
+ const sqlitePath = await question(rl, ' SQLite database path [.ragcode/graph.sqlite]: ');
42
+ config.sqlitePath = sqlitePath || '.ragcode/graph.sqlite';
43
+ }
44
+ // Semantic store
45
+ console.log('\nšŸ” Semantic Store (vector embeddings)');
46
+ const semanticStore = await question(rl, ' Choose: [1] LanceDB (persistent), [2] Memory (testing only) [1]: ');
47
+ config.semanticStore = semanticStore === '2' ? 'memory' : 'lancedb';
48
+ if (config.semanticStore === 'lancedb') {
49
+ const lancedbUri = await question(rl, ' LanceDB directory [.ragcode/lancedb]: ');
50
+ config.lancedbUri = lancedbUri || '.ragcode/lancedb';
51
+ }
52
+ // Embedding provider
53
+ console.log('\nšŸ¤– Embedding Provider');
54
+ const embeddingProvider = await question(rl, ' Choose: [1] Deterministic (offline), [2] OpenAI-compatible (requires API key) [1]: ');
55
+ config.embeddingProvider = embeddingProvider === '2' ? 'openai-compatible' : 'deterministic';
56
+ if (config.embeddingProvider === 'openai-compatible') {
57
+ console.log(' šŸ’” Set OPENAI_API_KEY environment variable or provide it now.');
58
+ const apiKey = await question(rl, ' OpenAI API Key (leave empty to use env var): ');
59
+ if (apiKey) {
60
+ config.openaiApiKey = apiKey;
61
+ }
62
+ }
63
+ rl.close();
64
+ return config;
65
+ }
66
+ function toRuntimeConfigFile(config) {
67
+ const configObj = {
68
+ graphStore: config.graphStore,
69
+ semanticStore: config.semanticStore,
70
+ embeddingProvider: config.embeddingProvider
71
+ };
72
+ if (config.sqlitePath)
73
+ configObj.sqlitePath = config.sqlitePath;
74
+ if (config.lancedbUri)
75
+ configObj.lancedbUri = config.lancedbUri;
76
+ if (config.openaiApiKey)
77
+ configObj.embeddingApiKey = config.openaiApiKey;
78
+ return configObj;
79
+ }
80
+ function writeConfigFile(targetDir, config) {
81
+ const ragcodeDir = path.join(targetDir, '.ragcode');
82
+ if (!fs.existsSync(ragcodeDir)) {
83
+ fs.mkdirSync(ragcodeDir, { recursive: true });
84
+ console.log(`\nšŸ“ Created directory: ${ragcodeDir}`);
85
+ }
86
+ const configPath = writeRuntimeConfigFile(targetDir, toRuntimeConfigFile(config));
87
+ console.log(`āœ… Configuration saved to: ${configPath}\n`);
88
+ return configPath;
89
+ }
90
+ function writeEnvExample(targetDir, config) {
91
+ const envPath = path.join(targetDir, '.ragcode', '.env.example');
92
+ let envContent = '# RagCode Environment Variables\n\n';
93
+ envContent += `RAGCODE_GRAPH_STORE=${config.graphStore}\n`;
94
+ if (config.sqlitePath) {
95
+ envContent += `RAGCODE_SQLITE_PATH=${config.sqlitePath}\n`;
96
+ }
97
+ envContent += `\nRAGCODE_SEMANTIC_STORE=${config.semanticStore}\n`;
98
+ if (config.lancedbUri) {
99
+ envContent += `RAGCODE_LANCEDB_URI=${config.lancedbUri}\n`;
100
+ }
101
+ envContent += `\nRAGCODE_EMBEDDING_PROVIDER=${config.embeddingProvider}\n`;
102
+ if (config.embeddingProvider === 'openai-compatible') {
103
+ envContent += 'OPENAI_API_KEY=your-api-key-here\n';
104
+ }
105
+ fs.writeFileSync(envPath, envContent, 'utf-8');
106
+ console.log(`šŸ“„ Environment template saved to: ${envPath}`);
107
+ }
108
+ function printNextSteps(config) {
109
+ console.log('\nšŸŽ‰ Setup complete!\n');
110
+ console.log('Next steps:');
111
+ console.log(' 1. Index your codebase:');
112
+ console.log(' ragcode index .\n');
113
+ console.log(' 2. Search code:');
114
+ console.log(' ragcode search . "your query"\n');
115
+ if (config.embeddingProvider === 'openai-compatible' && !config.openaiApiKey) {
116
+ console.log(' āš ļø Remember to set OPENAI_API_KEY:');
117
+ console.log(' export OPENAI_API_KEY=your-key\n');
118
+ }
119
+ console.log(' 3. Start the MCP server:');
120
+ console.log(' ragcode setup-mcp\n');
121
+ console.log(' 4. Upgrade embedding provider if needed:');
122
+ console.log(' ragcode configure\n');
123
+ console.log(' 5. Launch the web dashboard for observation:');
124
+ console.log(' ragcode dashboard\n');
125
+ }
126
+ export async function runInitConfig(options = {}) {
127
+ const targetDir = path.resolve(options.targetDir || process.cwd());
128
+ console.log(`šŸ“ Target directory: ${targetDir}\n`);
129
+ const config = options.defaults ? { ...DEFAULT_INIT_CONFIG } : await runWizard();
130
+ const configPath = writeConfigFile(targetDir, config);
131
+ writeEnvExample(targetDir, config);
132
+ printNextSteps(config);
133
+ return { targetDir, configPath, config: toRuntimeConfigFile(config) };
134
+ }
135
+ async function main() {
136
+ const args = process.argv.slice(2);
137
+ if (args.includes('--help') || args.includes('-h')) {
138
+ console.log(`
139
+ RagCode Configuration Wizard
140
+
141
+ Usage:
142
+ ragcode init [directory] [--defaults]
143
+
144
+ Examples:
145
+ ragcode init # Initialize in current directory
146
+ ragcode init /path/to/project
147
+ ragcode init --defaults # Write offline-first defaults without prompts
148
+ `);
149
+ process.exit(0);
150
+ }
151
+ const positional = args.filter((arg) => !arg.startsWith('-'));
152
+ await runInitConfig({
153
+ targetDir: positional[0],
154
+ defaults: args.includes('--defaults') || args.includes('--yes') || args.includes('-y')
155
+ });
156
+ }
157
+ const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : undefined;
158
+ if (invokedPath && invokedPath === fileURLToPath(import.meta.url)) {
159
+ main().catch(err => {
160
+ console.error('āŒ Setup failed:', err);
161
+ process.exit(1);
162
+ });
163
+ }
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Auto-configure RagCode as an MCP server for AI coding clients.
4
+ *
5
+ * Supported clients:
6
+ * - claude Claude Desktop JSON ~/.../claude_desktop_config.json (mcpServers.ragcode)
7
+ * - claude-code Claude Code (repo) JSON <cwd>/.mcp.json (mcpServers.ragcode)
8
+ * - codex Codex CLI TOML ~/.codex/config.toml (mcp_servers.ragcode)
9
+ * - generic print only JSON (paste into any MCP client)
10
+ *
11
+ * Merge strategy: existing config is parsed, the `ragcode` entry is upserted, and the
12
+ * file is rewritten. Other servers and unrelated keys are preserved. The previous file
13
+ * is backed up alongside the original before any overwrite.
14
+ */
15
+ export type McpClient = 'claude' | 'claude-code' | 'codex' | 'generic';
16
+ interface MCPServerConfig {
17
+ command: string;
18
+ args: string[];
19
+ cwd?: string;
20
+ env?: Record<string, string>;
21
+ }
22
+ interface MCPConfig {
23
+ mcpServers: Record<string, MCPServerConfig>;
24
+ }
25
+ export interface SetupMcpOptions {
26
+ configPath?: string;
27
+ print?: boolean;
28
+ includeSecrets?: boolean;
29
+ client?: McpClient;
30
+ /** Skip the interactive overwrite prompt and replace any existing ragcode entry. */
31
+ force?: boolean;
32
+ cwd?: string;
33
+ env?: NodeJS.ProcessEnv;
34
+ }
35
+ export declare function getClaudeMCPConfigPath(): string;
36
+ /** Claude Code reads project-scoped MCP servers from `<repoRoot>/.mcp.json`. */
37
+ export declare function getClaudeCodeMCPConfigPath(cwd: string): string;
38
+ /** Codex CLI reads MCP servers from `~/.codex/config.toml` (CODEX_HOME overrides ~/.codex). */
39
+ export declare function getCodexConfigPath(env?: NodeJS.ProcessEnv): string;
40
+ export declare function buildMcpServerConfig(options?: SetupMcpOptions): MCPServerConfig;
41
+ /**
42
+ * Upsert the ragcode server into a JSON MCP config object (Claude Desktop / Claude Code).
43
+ * Unknown top-level keys and other servers are preserved. Returns a new object.
44
+ */
45
+ export declare function mergeMcpServersJson(existing: unknown, server: MCPServerConfig): MCPConfig;
46
+ /**
47
+ * Upsert the ragcode server into a Codex config.toml string.
48
+ * Parses existing TOML, sets `mcp_servers.ragcode`, and re-stringifies. Other tables and
49
+ * keys are preserved; TOML comments are not (smol-toml limitation — callers back up first).
50
+ */
51
+ export declare function mergeCodexToml(existingToml: string, server: MCPServerConfig): string;
52
+ export declare function setupMCP(options?: SetupMcpOptions): void;
53
+ export {};
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Auto-configure RagCode as an MCP server for AI coding clients.
4
+ *
5
+ * Supported clients:
6
+ * - claude Claude Desktop JSON ~/.../claude_desktop_config.json (mcpServers.ragcode)
7
+ * - claude-code Claude Code (repo) JSON <cwd>/.mcp.json (mcpServers.ragcode)
8
+ * - codex Codex CLI TOML ~/.codex/config.toml (mcp_servers.ragcode)
9
+ * - generic print only JSON (paste into any MCP client)
10
+ *
11
+ * Merge strategy: existing config is parsed, the `ragcode` entry is upserted, and the
12
+ * file is rewritten. Other servers and unrelated keys are preserved. The previous file
13
+ * is backed up alongside the original before any overwrite.
14
+ */
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import os from 'node:os';
18
+ import { execFileSync } from 'node:child_process';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
21
+ import { loadRuntimeConfig, runtimeConfigToEnv } from '../src/config/runtime-config.js';
22
+ const SERVER_KEY = 'ragcode';
23
+ // ---------------------------------------------------------------------------
24
+ // Config-path resolution (one per client)
25
+ // ---------------------------------------------------------------------------
26
+ export function getClaudeMCPConfigPath() {
27
+ const platform = os.platform();
28
+ if (platform === 'win32') {
29
+ return path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
30
+ }
31
+ else if (platform === 'darwin') {
32
+ return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
33
+ }
34
+ else {
35
+ // Linux
36
+ return path.join(os.homedir(), '.config', 'claude', 'claude_desktop_config.json');
37
+ }
38
+ }
39
+ /** Claude Code reads project-scoped MCP servers from `<repoRoot>/.mcp.json`. */
40
+ export function getClaudeCodeMCPConfigPath(cwd) {
41
+ return path.join(cwd, '.mcp.json');
42
+ }
43
+ /** Codex CLI reads MCP servers from `~/.codex/config.toml` (CODEX_HOME overrides ~/.codex). */
44
+ export function getCodexConfigPath(env = process.env) {
45
+ const home = env.CODEX_HOME && env.CODEX_HOME.trim().length > 0
46
+ ? env.CODEX_HOME
47
+ : path.join(os.homedir(), '.codex');
48
+ return path.join(home, 'config.toml');
49
+ }
50
+ function resolveConfigPath(client, options) {
51
+ if (options.configPath)
52
+ return options.configPath;
53
+ const cwd = options.cwd ?? process.cwd();
54
+ switch (client) {
55
+ case 'codex':
56
+ return getCodexConfigPath(options.env ?? process.env);
57
+ case 'claude-code':
58
+ return getClaudeCodeMCPConfigPath(cwd);
59
+ case 'claude':
60
+ default:
61
+ return getClaudeMCPConfigPath();
62
+ }
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Server config (shared core — unchanged contract, tests depend on this)
66
+ // ---------------------------------------------------------------------------
67
+ function getRagCodeCommand() {
68
+ // Check if ragcode is globally installed
69
+ try {
70
+ execFileSync('ragcode', ['--version'], { stdio: 'ignore' });
71
+ return 'ragcode';
72
+ }
73
+ catch {
74
+ // Fallback to npx
75
+ return 'npx';
76
+ }
77
+ }
78
+ export function buildMcpServerConfig(options = {}) {
79
+ const command = getRagCodeCommand();
80
+ const args = command === 'npx' ? ['ragcode-context-engine', 'mcp'] : ['mcp'];
81
+ const runtime = loadRuntimeConfig({ cwd: options.cwd ?? process.cwd(), env: options.env ?? process.env });
82
+ return {
83
+ command,
84
+ args,
85
+ cwd: runtime.repoRoot,
86
+ env: runtimeConfigToEnv(runtime, { includeSecrets: options.includeSecrets })
87
+ };
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // Pure merge functions (no IO — unit-testable)
91
+ // ---------------------------------------------------------------------------
92
+ /**
93
+ * Upsert the ragcode server into a JSON MCP config object (Claude Desktop / Claude Code).
94
+ * Unknown top-level keys and other servers are preserved. Returns a new object.
95
+ */
96
+ export function mergeMcpServersJson(existing, server) {
97
+ const base = existing && typeof existing === 'object' ? { ...existing } : {};
98
+ const servers = base.mcpServers && typeof base.mcpServers === 'object'
99
+ ? { ...base.mcpServers }
100
+ : {};
101
+ servers[SERVER_KEY] = server;
102
+ return { ...base, mcpServers: servers };
103
+ }
104
+ /**
105
+ * Upsert the ragcode server into a Codex config.toml string.
106
+ * Parses existing TOML, sets `mcp_servers.ragcode`, and re-stringifies. Other tables and
107
+ * keys are preserved; TOML comments are not (smol-toml limitation — callers back up first).
108
+ */
109
+ export function mergeCodexToml(existingToml, server) {
110
+ let root = {};
111
+ const trimmed = existingToml.trim();
112
+ if (trimmed.length > 0) {
113
+ const parsed = parseToml(existingToml);
114
+ if (parsed && typeof parsed === 'object') {
115
+ root = parsed;
116
+ }
117
+ }
118
+ const mcpServers = root.mcp_servers && typeof root.mcp_servers === 'object'
119
+ ? { ...root.mcp_servers }
120
+ : {};
121
+ // Codex stdio server schema: command, args, env. cwd is honored by recent Codex builds and
122
+ // ignored by older ones; the env values already carry absolute paths so behavior is correct
123
+ // either way. Drop undefined keys so the emitted TOML stays clean.
124
+ const entry = {
125
+ command: server.command,
126
+ args: server.args
127
+ };
128
+ if (server.cwd)
129
+ entry.cwd = server.cwd;
130
+ if (server.env && Object.keys(server.env).length > 0)
131
+ entry.env = server.env;
132
+ mcpServers[SERVER_KEY] = entry;
133
+ root.mcp_servers = mcpServers;
134
+ return stringifyToml(root);
135
+ }
136
+ function isJsonClient(client) {
137
+ return client === 'claude' || client === 'claude-code';
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // IO helpers
141
+ // ---------------------------------------------------------------------------
142
+ function ensureDir(filePath) {
143
+ const dir = path.dirname(filePath);
144
+ if (!fs.existsSync(dir)) {
145
+ console.log(`šŸ“ Creating config directory: ${dir}`);
146
+ fs.mkdirSync(dir, { recursive: true });
147
+ }
148
+ }
149
+ function backupExisting(filePath) {
150
+ if (!fs.existsSync(filePath))
151
+ return;
152
+ const backup = `${filePath}.ragcode-backup`;
153
+ fs.copyFileSync(filePath, backup);
154
+ console.log(`šŸ—‚ļø Backed up existing config to: ${backup}`);
155
+ }
156
+ function readFileSafe(filePath) {
157
+ try {
158
+ if (fs.existsSync(filePath))
159
+ return fs.readFileSync(filePath, 'utf-8');
160
+ }
161
+ catch (err) {
162
+ console.warn(`āš ļø Failed to read existing config: ${err}`);
163
+ }
164
+ return undefined;
165
+ }
166
+ /** Detect whether a ragcode entry already exists in the on-disk config. */
167
+ function hasExistingEntry(client, raw) {
168
+ if (!raw)
169
+ return false;
170
+ try {
171
+ if (isJsonClient(client)) {
172
+ const parsed = JSON.parse(raw);
173
+ return Boolean(parsed.mcpServers && SERVER_KEY in parsed.mcpServers);
174
+ }
175
+ const parsed = parseToml(raw);
176
+ return Boolean(parsed.mcp_servers && SERVER_KEY in parsed.mcp_servers);
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ }
182
+ // ---------------------------------------------------------------------------
183
+ // Public entry point
184
+ // ---------------------------------------------------------------------------
185
+ export function setupMCP(options = {}) {
186
+ const client = options.client ?? 'claude';
187
+ validateClient(client);
188
+ if (options.print || client === 'generic') {
189
+ printConfig(options);
190
+ return;
191
+ }
192
+ const configPath = resolveConfigPath(client, options);
193
+ const server = buildMcpServerConfig(options);
194
+ const raw = readFileSafe(configPath);
195
+ console.log(`šŸ“ ${clientLabel(client)} config path: ${configPath}`);
196
+ if (hasExistingEntry(client, raw) && !options.force) {
197
+ if (!process.stdin.isTTY) {
198
+ console.log('āš ļø RagCode is already configured here. Re-run with --force to overwrite, or remove the existing entry first.');
199
+ return;
200
+ }
201
+ void import('node:readline').then((readline) => {
202
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
203
+ rl.question('\nāš ļø RagCode is already configured. Overwrite existing entry? (y/N): ', (answer) => {
204
+ rl.close();
205
+ if (answer.trim().toLowerCase() !== 'y') {
206
+ console.log('āŒ Setup cancelled.');
207
+ return;
208
+ }
209
+ writeClientConfig(client, configPath, raw, server);
210
+ });
211
+ });
212
+ return;
213
+ }
214
+ writeClientConfig(client, configPath, raw, server);
215
+ }
216
+ function writeClientConfig(client, configPath, raw, server) {
217
+ ensureDir(configPath);
218
+ backupExisting(configPath);
219
+ let contents;
220
+ if (isJsonClient(client)) {
221
+ let existing = {};
222
+ if (raw) {
223
+ try {
224
+ existing = JSON.parse(raw);
225
+ }
226
+ catch (err) {
227
+ console.warn(`āš ļø Existing config is not valid JSON; starting fresh. (${err})`);
228
+ }
229
+ }
230
+ contents = `${JSON.stringify(mergeMcpServersJson(existing, server), null, 2)}\n`;
231
+ }
232
+ else {
233
+ // codex
234
+ contents = mergeCodexToml(raw ?? '', server);
235
+ }
236
+ try {
237
+ fs.writeFileSync(configPath, contents, 'utf-8');
238
+ }
239
+ catch (err) {
240
+ console.error(`āŒ Failed to write config: ${err}`);
241
+ process.exitCode = 1;
242
+ return;
243
+ }
244
+ console.log(`\nāœ… RagCode MCP server configured for ${clientLabel(client)}.`);
245
+ console.log('\nšŸ“ Server entry:');
246
+ console.log(JSON.stringify(server, null, 2));
247
+ if (server.env?.RAGCODE_EMBEDDING_API_KEY === '<redacted>') {
248
+ console.log('\nāš ļø API key was redacted. Re-run with --include-secrets to embed it, or set the key in your environment.');
249
+ }
250
+ console.log(`\nšŸ”„ ${restartHint(client)}`);
251
+ }
252
+ function printConfig(options = {}) {
253
+ const client = options.client ?? 'generic';
254
+ const server = buildMcpServerConfig(options);
255
+ if (client === 'codex') {
256
+ console.log('Add this to your Codex config (~/.codex/config.toml):\n');
257
+ console.log(mergeCodexToml('', server));
258
+ return;
259
+ }
260
+ console.log('Add this to your MCP client config:\n');
261
+ console.log(JSON.stringify({ mcpServers: { [SERVER_KEY]: server } }, null, 2));
262
+ }
263
+ function clientLabel(client) {
264
+ switch (client) {
265
+ case 'claude':
266
+ return 'Claude Desktop';
267
+ case 'claude-code':
268
+ return 'Claude Code';
269
+ case 'codex':
270
+ return 'Codex';
271
+ case 'generic':
272
+ return 'generic MCP client';
273
+ }
274
+ }
275
+ function restartHint(client) {
276
+ switch (client) {
277
+ case 'claude':
278
+ return 'Restart Claude Desktop to activate the MCP server.';
279
+ case 'claude-code':
280
+ return 'Reopen the project in Claude Code (it loads .mcp.json on startup) to activate the server.';
281
+ case 'codex':
282
+ return 'Restart your Codex session to activate the MCP server.';
283
+ default:
284
+ return 'Restart your client to activate the MCP server.';
285
+ }
286
+ }
287
+ // ---------------------------------------------------------------------------
288
+ // CLI
289
+ // ---------------------------------------------------------------------------
290
+ function printHelp() {
291
+ console.log(`
292
+ RagCode MCP Setup
293
+
294
+ Usage:
295
+ ragcode setup-mcp [options]
296
+
297
+ Options:
298
+ --config <path> Custom config path (overrides the client default)
299
+ --print Print config without writing
300
+ --include-secrets Include real secrets instead of redacted placeholders
301
+ --client <client> Target client: claude, claude-code, codex, or generic (default: claude)
302
+ --force Overwrite an existing ragcode entry without prompting
303
+ --help, -h Show this help
304
+
305
+ Examples:
306
+ ragcode setup-mcp # Claude Desktop (default)
307
+ ragcode setup-mcp --client claude-code # project .mcp.json for Claude Code
308
+ ragcode setup-mcp --client codex # ~/.codex/config.toml
309
+ ragcode setup-mcp --client codex --print # print TOML, write nothing
310
+ ragcode setup-mcp --config ~/custom.json # custom path
311
+ `);
312
+ }
313
+ function parseOptions(args) {
314
+ const configIndex = args.indexOf('--config');
315
+ const clientIndex = args.indexOf('--client');
316
+ const client = clientIndex >= 0 ? args[clientIndex + 1] : undefined;
317
+ validateClient(client);
318
+ return {
319
+ print: args.includes('--print'),
320
+ includeSecrets: args.includes('--include-secrets'),
321
+ force: args.includes('--force'),
322
+ configPath: configIndex >= 0 ? args[configIndex + 1] : undefined,
323
+ client: client
324
+ };
325
+ }
326
+ function validateClient(client) {
327
+ if (client && !['claude', 'claude-code', 'codex', 'generic'].includes(client)) {
328
+ throw new Error(`Unsupported MCP client: ${client} (expected claude, claude-code, codex, or generic)`);
329
+ }
330
+ }
331
+ const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : undefined;
332
+ if (invokedPath && invokedPath === fileURLToPath(import.meta.url)) {
333
+ const args = process.argv.slice(2);
334
+ if (args.includes('--help') || args.includes('-h')) {
335
+ printHelp();
336
+ process.exit(0);
337
+ }
338
+ try {
339
+ setupMCP(parseOptions(args));
340
+ }
341
+ catch (error) {
342
+ console.error(`āŒ MCP setup failed: ${error instanceof Error ? error.message : String(error)}`);
343
+ process.exit(1);
344
+ }
345
+ }
@@ -2,6 +2,7 @@
2
2
  import path from "node:path";
3
3
  import { Command } from "commander";
4
4
  import { loadDotEnv } from "../config/dotenv.js";
5
+ import { getPackageVersion } from "../config/package-info.js";
5
6
  import { createRuntimeComponentsForRepo } from "../config/runtime-config.js";
6
7
  import { RagCodeEngine } from "../core/engine.js";
7
8
  import { runConfigureCommand } from "./configure.js";
@@ -17,7 +18,7 @@ import { setupMCP } from "../../scripts/setup-mcp.js";
17
18
  import { runUpdate } from "./update.js";
18
19
  loadDotEnv();
19
20
  const program = new Command();
20
- program.name("ragcode").description("Local code intelligence context engine").version("0.1.0");
21
+ program.name("ragcode").description("Local code intelligence context engine").version(getPackageVersion());
21
22
  program
22
23
  .command("index")
23
24
  .argument("<repoRoot>")
@@ -1,4 +1,5 @@
1
- export declare const PACKAGE_NAME = "ragcode-context-engine";
1
+ import { PACKAGE_NAME } from "../config/package-info.js";
2
+ export { PACKAGE_NAME };
2
3
  export interface UpdateOptions {
3
4
  /** Only report current vs latest; don't install. */
4
5
  checkOnly?: boolean;
@@ -1,27 +1,8 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { createRequire } from "node:module";
3
+ import { getPackageVersion, PACKAGE_NAME } from "../config/package-info.js";
4
4
  const execFileAsync = promisify(execFile);
5
- // `ragcode update` upgrades the globally-installed CLI in place. We don't bundle an auto-updater;
6
- // we shell out to the package manager that owns the global install. The published package name is
7
- // `ragcode-context-engine` (see package.json), which is what npm/pnpm/yarn install globally.
8
- export const PACKAGE_NAME = "ragcode-context-engine";
9
- function currentVersion() {
10
- // The package.json depth differs between source (src/cli/update.ts -> ../../package.json) and the
11
- // built layout (dist/src/cli/update.js -> ../../../package.json), so try both rather than guessing.
12
- const require = createRequire(import.meta.url);
13
- for (const candidate of ["../../package.json", "../../../package.json"]) {
14
- try {
15
- const pkg = require(candidate);
16
- if (pkg.name === PACKAGE_NAME && pkg.version)
17
- return pkg.version;
18
- }
19
- catch {
20
- // try the next candidate
21
- }
22
- }
23
- return "unknown";
24
- }
5
+ export { PACKAGE_NAME };
25
6
  // Auto-detect the package manager from the npm_config_user_agent the parent PM exposes, falling
26
7
  // back to npm. This keeps a pnpm-global install from being "updated" by npm into a broken state.
27
8
  function detectPackageManager(env = process.env) {
@@ -55,7 +36,7 @@ function installArgv(pm, spec) {
55
36
  }
56
37
  }
57
38
  export async function runUpdate(options = {}) {
58
- const current = currentVersion();
39
+ const current = getPackageVersion();
59
40
  const pm = options.packageManager ?? detectPackageManager();
60
41
  const target = options.version ?? "latest";
61
42
  const latest = target === "latest" ? await fetchLatestVersion() : target;
@@ -0,0 +1,2 @@
1
+ export declare const PACKAGE_NAME = "ragcode-context-engine";
2
+ export declare function getPackageVersion(): string;
@@ -0,0 +1,16 @@
1
+ import { createRequire } from "node:module";
2
+ export const PACKAGE_NAME = "ragcode-context-engine";
3
+ export function getPackageVersion() {
4
+ const require = createRequire(import.meta.url);
5
+ for (const candidate of ["../../package.json", "../../../package.json"]) {
6
+ try {
7
+ const pkg = require(candidate);
8
+ if (pkg.name === PACKAGE_NAME && pkg.version)
9
+ return pkg.version;
10
+ }
11
+ catch {
12
+ // Source and built layouts have different depths; try the next candidate.
13
+ }
14
+ }
15
+ return "unknown";
16
+ }
@@ -1,12 +1,13 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { getPackageVersion } from "../config/package-info.js";
3
4
  import { createRuntimeComponentsForRepo } from "../config/runtime-config.js";
4
5
  import { RagCodeEngine } from "../core/engine.js";
5
6
  import { callTool, listRuntimeToolDefinitions } from "./tools.js";
6
7
  export function createMcpServer(engine, options = {}) {
7
8
  const server = new McpServer({
8
9
  name: options.name ?? "ragcode-context-engine",
9
- version: options.version ?? "0.1.0"
10
+ version: options.version ?? getPackageVersion()
10
11
  });
11
12
  for (const tool of listRuntimeToolDefinitions()) {
12
13
  server.registerTool(tool.name, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragcode-context-engine",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local code intelligence foundation: structural code graph, LanceDB semantic layer, retrieval, context packing, and MCP tools.",
@@ -31,6 +31,7 @@
31
31
  },
32
32
  "files": [
33
33
  "dist/src",
34
+ "dist/scripts",
34
35
  "README.md",
35
36
  "LICENSE"
36
37
  ],