@getreka/cli 0.1.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/dist/api.d.ts +7 -0
- package/dist/api.js +68 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.js +30 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +117 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.js +108 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.js +48 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +64 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +106 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +98 -0
- package/package.json +42 -0
package/dist/api.d.ts
ADDED
package/dist/api.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* API client for Reka CLI
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.createClient = createClient;
|
|
40
|
+
exports.formatError = formatError;
|
|
41
|
+
const axios_1 = __importStar(require("axios"));
|
|
42
|
+
function createClient(config) {
|
|
43
|
+
const headers = {
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
"X-Project-Name": config.project.name,
|
|
46
|
+
"X-Project-Path": config.project.path,
|
|
47
|
+
};
|
|
48
|
+
if (config.api.key) {
|
|
49
|
+
headers["Authorization"] = `Bearer ${config.api.key}`;
|
|
50
|
+
}
|
|
51
|
+
return axios_1.default.create({
|
|
52
|
+
baseURL: config.api.url,
|
|
53
|
+
timeout: 120000,
|
|
54
|
+
headers,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function formatError(err) {
|
|
58
|
+
if (err instanceof axios_1.AxiosError) {
|
|
59
|
+
if (err.code === "ECONNREFUSED") {
|
|
60
|
+
return `Cannot connect to Reka API at ${err.config?.baseURL}. Is it running?`;
|
|
61
|
+
}
|
|
62
|
+
if (err.response) {
|
|
63
|
+
const data = err.response.data;
|
|
64
|
+
return data?.error || data?.message || `HTTP ${err.response.status}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return err.message || String(err);
|
|
68
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.indexCommand = indexCommand;
|
|
7
|
+
const ora_1 = __importDefault(require("ora"));
|
|
8
|
+
const api_1 = require("../api");
|
|
9
|
+
async function indexCommand(client, config, opts) {
|
|
10
|
+
const indexPath = opts.path || config.project.path;
|
|
11
|
+
const spinner = (0, ora_1.default)(`Indexing ${indexPath}...`).start();
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await client.post("/api/index", {
|
|
14
|
+
path: indexPath,
|
|
15
|
+
projectName: config.project.name,
|
|
16
|
+
});
|
|
17
|
+
spinner.succeed(`Indexed ${data.filesProcessed || "N/A"} files`);
|
|
18
|
+
if (data.stats) {
|
|
19
|
+
console.log("");
|
|
20
|
+
console.log(` Chunks: ${data.stats.chunks || "N/A"}`);
|
|
21
|
+
console.log(` Symbols: ${data.stats.symbols || "N/A"}`);
|
|
22
|
+
console.log(` Duration: ${data.stats.duration || "N/A"}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
spinner.fail(`Index failed: ${(0, api_1.formatError)(err)}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
console.log("");
|
|
30
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.initCommand = initCommand;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const api_1 = require("../api");
|
|
44
|
+
const config_1 = require("../config");
|
|
45
|
+
const MCP_TEMPLATE = `{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"reka": {
|
|
48
|
+
"command": "npx",
|
|
49
|
+
"args": ["-y", "@getreka/mcp"],
|
|
50
|
+
"env": {
|
|
51
|
+
"REKA_API_KEY": "{{API_KEY}}"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}`;
|
|
56
|
+
async function initCommand(opts) {
|
|
57
|
+
const projectPath = opts.path || process.cwd();
|
|
58
|
+
const projectName = opts.project || path.basename(projectPath);
|
|
59
|
+
// Cloud mode: use provided key, skip keygen
|
|
60
|
+
if (opts.cloud || opts.key) {
|
|
61
|
+
if (!opts.key) {
|
|
62
|
+
console.log(chalk_1.default.red("\n --key is required for cloud mode. Get one at https://getreka.dev/dashboard\n"));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
writeMcpConfig(projectPath, opts.key, opts.force);
|
|
66
|
+
console.log(chalk_1.default.green(`\n ✓ Connected to Reka Cloud`));
|
|
67
|
+
console.log(` Project will be resolved from your API key.\n`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Self-hosted: generate key via local API
|
|
71
|
+
const config = (0, config_1.loadConfig)({
|
|
72
|
+
api: { url: opts.apiUrl || "http://localhost:3100" },
|
|
73
|
+
project: { name: projectName, path: projectPath },
|
|
74
|
+
});
|
|
75
|
+
const client = (0, api_1.createClient)(config);
|
|
76
|
+
console.log(`\n Generating API key for project ${chalk_1.default.bold(projectName)}...`);
|
|
77
|
+
try {
|
|
78
|
+
const { data } = await client.post("/api/keys", {
|
|
79
|
+
projectName,
|
|
80
|
+
label: `init-${Date.now()}`,
|
|
81
|
+
});
|
|
82
|
+
const apiKey = data.key;
|
|
83
|
+
writeMcpConfig(projectPath, apiKey, opts.force);
|
|
84
|
+
console.log(chalk_1.default.green(` ✓ API key created: ${apiKey.slice(0, 20)}...`));
|
|
85
|
+
console.log(chalk_1.default.green(` ✓ .mcp.json written`));
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log(" Your AI assistant now has memory. Try asking it about your codebase!");
|
|
88
|
+
console.log("");
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const msg = err.response?.data?.error || err.message;
|
|
92
|
+
console.log(chalk_1.default.red(`\n Failed to generate key: ${msg}`));
|
|
93
|
+
console.log(chalk_1.default.yellow(` Is the Reka API running? Start with: docker-compose up -d\n`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function writeMcpConfig(projectPath, apiKey, force) {
|
|
97
|
+
const mcpPath = path.join(projectPath, ".mcp.json");
|
|
98
|
+
if (fs.existsSync(mcpPath) && !force) {
|
|
99
|
+
// Merge into existing .mcp.json
|
|
100
|
+
try {
|
|
101
|
+
const existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
102
|
+
existing.mcpServers = existing.mcpServers || {};
|
|
103
|
+
existing.mcpServers.reka = {
|
|
104
|
+
command: "npx",
|
|
105
|
+
args: ["-y", "@getreka/mcp"],
|
|
106
|
+
env: { REKA_API_KEY: apiKey },
|
|
107
|
+
};
|
|
108
|
+
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Fall through to overwrite
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const content = MCP_TEMPLATE.replace("{{API_KEY}}", apiKey);
|
|
116
|
+
fs.writeFileSync(mcpPath, content + "\n", "utf-8");
|
|
117
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.modelsListCommand = modelsListCommand;
|
|
7
|
+
exports.modelsTestCommand = modelsTestCommand;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const api_1 = require("../api");
|
|
12
|
+
async function checkProvider(name, url) {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
try {
|
|
15
|
+
if (name === "ollama") {
|
|
16
|
+
const { data } = await axios_1.default.get(`${url}/api/tags`, { timeout: 5000 });
|
|
17
|
+
return {
|
|
18
|
+
name,
|
|
19
|
+
url,
|
|
20
|
+
status: "healthy",
|
|
21
|
+
latency: Date.now() - start,
|
|
22
|
+
models: (data.models || []).map((m) => m.name),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
else if (name === "bge-m3") {
|
|
26
|
+
await axios_1.default.get(`${url}/health`, { timeout: 5000 });
|
|
27
|
+
return { name, url, status: "healthy", latency: Date.now() - start };
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await axios_1.default.get(url, { timeout: 5000 });
|
|
31
|
+
return { name, url, status: "healthy", latency: Date.now() - start };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { name, url, status: "unreachable", latency: Date.now() - start };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function modelsListCommand(config) {
|
|
39
|
+
console.log(chalk_1.default.bold("\n Model Providers\n"));
|
|
40
|
+
const spinner = (0, ora_1.default)("Checking providers...").start();
|
|
41
|
+
const checks = [];
|
|
42
|
+
// Always check default providers
|
|
43
|
+
const ollamaUrl = config.models?.llm?.utility?.url || "http://localhost:11434";
|
|
44
|
+
const bgeUrl = config.models?.embeddings?.url || "http://localhost:8080";
|
|
45
|
+
checks.push(checkProvider("ollama", ollamaUrl));
|
|
46
|
+
checks.push(checkProvider("bge-m3", bgeUrl));
|
|
47
|
+
const results = await Promise.all(checks);
|
|
48
|
+
spinner.stop();
|
|
49
|
+
for (const r of results) {
|
|
50
|
+
const icon = r.status === "healthy" ? chalk_1.default.green("●") : chalk_1.default.red("●");
|
|
51
|
+
const latency = r.latency ? chalk_1.default.gray(`${r.latency}ms`) : "";
|
|
52
|
+
console.log(` ${icon} ${chalk_1.default.bold(r.name)} ${chalk_1.default.gray(r.url)} ${latency}`);
|
|
53
|
+
if (r.models && r.models.length > 0) {
|
|
54
|
+
for (const m of r.models.slice(0, 10)) {
|
|
55
|
+
console.log(` ${chalk_1.default.gray("→")} ${m}`);
|
|
56
|
+
}
|
|
57
|
+
if (r.models.length > 10) {
|
|
58
|
+
console.log(` ${chalk_1.default.gray(` ...and ${r.models.length - 10} more`)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Show configured routing
|
|
63
|
+
if (config.models?.llm) {
|
|
64
|
+
console.log(chalk_1.default.bold("\n Routing"));
|
|
65
|
+
const llm = config.models.llm;
|
|
66
|
+
if (llm.utility)
|
|
67
|
+
console.log(` Utility: ${llm.utility.provider} / ${llm.utility.model || "default"}`);
|
|
68
|
+
if (llm.standard)
|
|
69
|
+
console.log(` Standard: ${llm.standard.provider} / ${llm.standard.model || "default"}`);
|
|
70
|
+
if (llm.complex)
|
|
71
|
+
console.log(` Complex: ${llm.complex.provider} / ${llm.complex.model || "default"}`);
|
|
72
|
+
}
|
|
73
|
+
console.log("");
|
|
74
|
+
}
|
|
75
|
+
async function modelsTestCommand(config) {
|
|
76
|
+
console.log(chalk_1.default.bold("\n Testing Model Connections\n"));
|
|
77
|
+
const ollamaUrl = config.models?.llm?.utility?.url || "http://localhost:11434";
|
|
78
|
+
const bgeUrl = config.models?.embeddings?.url || "http://localhost:8080";
|
|
79
|
+
// Test embedding
|
|
80
|
+
const embedSpinner = (0, ora_1.default)("Testing embeddings...").start();
|
|
81
|
+
try {
|
|
82
|
+
const start = Date.now();
|
|
83
|
+
await axios_1.default.post(`${bgeUrl}/embed`, {
|
|
84
|
+
text: "test embedding connection",
|
|
85
|
+
}, { timeout: 10000 });
|
|
86
|
+
embedSpinner.succeed(`Embeddings OK (${Date.now() - start}ms)`);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
embedSpinner.fail(`Embeddings failed: ${(0, api_1.formatError)(err)}`);
|
|
90
|
+
}
|
|
91
|
+
// Test LLM
|
|
92
|
+
const llmSpinner = (0, ora_1.default)("Testing LLM...").start();
|
|
93
|
+
try {
|
|
94
|
+
const model = config.models?.llm?.utility?.model || "qwen3.5:35b";
|
|
95
|
+
const start = Date.now();
|
|
96
|
+
await axios_1.default.post(`${ollamaUrl}/api/generate`, {
|
|
97
|
+
model,
|
|
98
|
+
prompt: 'Say "ok" and nothing else.',
|
|
99
|
+
stream: false,
|
|
100
|
+
options: { num_predict: 10 },
|
|
101
|
+
}, { timeout: 30000 });
|
|
102
|
+
llmSpinner.succeed(`LLM OK — ${model} (${Date.now() - start}ms)`);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
llmSpinner.fail(`LLM failed: ${(0, api_1.formatError)(err)}`);
|
|
106
|
+
}
|
|
107
|
+
console.log("");
|
|
108
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.searchCommand = searchCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const api_1 = require("../api");
|
|
10
|
+
async function searchCommand(client, config, query, opts) {
|
|
11
|
+
const limit = parseInt(opts.limit || "5", 10);
|
|
12
|
+
const spinner = (0, ora_1.default)("Searching...").start();
|
|
13
|
+
try {
|
|
14
|
+
const { data } = await client.post("/api/search", {
|
|
15
|
+
query,
|
|
16
|
+
limit,
|
|
17
|
+
collection: opts.type || "codebase",
|
|
18
|
+
});
|
|
19
|
+
spinner.stop();
|
|
20
|
+
const results = data.results || data;
|
|
21
|
+
if (!results || results.length === 0) {
|
|
22
|
+
console.log(chalk_1.default.yellow("\n No results found.\n"));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.log(chalk_1.default.bold(`\n ${results.length} results for "${query}"\n`));
|
|
26
|
+
for (const [i, r] of results.entries()) {
|
|
27
|
+
const score = r.score
|
|
28
|
+
? chalk_1.default.gray(`(${(r.score * 100).toFixed(0)}%)`)
|
|
29
|
+
: "";
|
|
30
|
+
const filePath = r.metadata?.filePath || r.metadata?.file_path || "";
|
|
31
|
+
const lines = r.metadata?.startLine
|
|
32
|
+
? `:${r.metadata.startLine}-${r.metadata.endLine || ""}`
|
|
33
|
+
: "";
|
|
34
|
+
console.log(` ${chalk_1.default.blue(`${i + 1}.`)} ${chalk_1.default.bold(filePath)}${lines} ${score}`);
|
|
35
|
+
// Show snippet (first 3 lines)
|
|
36
|
+
if (r.content || r.text) {
|
|
37
|
+
const text = (r.content || r.text);
|
|
38
|
+
const snippet = text.split("\n").slice(0, 3).join("\n");
|
|
39
|
+
console.log(chalk_1.default.gray(` ${snippet.replace(/\n/g, "\n ")}`));
|
|
40
|
+
}
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
spinner.fail(`Search failed: ${(0, api_1.formatError)(err)}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.statusCommand = statusCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const api_1 = require("../api");
|
|
9
|
+
async function statusCommand(client, config) {
|
|
10
|
+
console.log(chalk_1.default.bold("\n Reka Status\n"));
|
|
11
|
+
console.log(` API: ${config.api.url}`);
|
|
12
|
+
console.log(` Project: ${config.project.name}`);
|
|
13
|
+
console.log(` Path: ${config.project.path}`);
|
|
14
|
+
console.log("");
|
|
15
|
+
// Check API health
|
|
16
|
+
try {
|
|
17
|
+
const { data } = await client.get("/health");
|
|
18
|
+
console.log(` API: ${chalk_1.default.green("● healthy")}`);
|
|
19
|
+
if (data.cache) {
|
|
20
|
+
console.log(` Cache: ${chalk_1.default.green("● connected")} (${data.cache.hitRate || "N/A"} hit rate)`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.log(` API: ${chalk_1.default.red("● unreachable")} — ${(0, api_1.formatError)(err)}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Check services
|
|
28
|
+
try {
|
|
29
|
+
const { data } = await client.get("/api/project/stats");
|
|
30
|
+
const stats = data.stats || data;
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log(chalk_1.default.bold(" Project Stats"));
|
|
33
|
+
if (stats.collections) {
|
|
34
|
+
console.log(` Collections: ${stats.collections}`);
|
|
35
|
+
}
|
|
36
|
+
if (stats.totalPoints !== undefined) {
|
|
37
|
+
console.log(` Vectors: ${stats.totalPoints.toLocaleString()}`);
|
|
38
|
+
}
|
|
39
|
+
if (stats.memoryCount !== undefined) {
|
|
40
|
+
console.log(` Memories: ${stats.memoryCount}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Stats endpoint may not exist, that's ok
|
|
45
|
+
}
|
|
46
|
+
// Check index status
|
|
47
|
+
try {
|
|
48
|
+
const { data } = await client.get("/api/index/status");
|
|
49
|
+
if (data.status) {
|
|
50
|
+
const s = data.status;
|
|
51
|
+
console.log("");
|
|
52
|
+
console.log(chalk_1.default.bold(" Index"));
|
|
53
|
+
console.log(` Status: ${s.indexing ? chalk_1.default.yellow("● indexing") : chalk_1.default.green("● idle")}`);
|
|
54
|
+
if (s.totalFiles)
|
|
55
|
+
console.log(` Files: ${s.totalFiles}`);
|
|
56
|
+
if (s.lastIndexed)
|
|
57
|
+
console.log(` Last run: ${new Date(s.lastIndexed).toLocaleString()}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Index status may not exist
|
|
62
|
+
}
|
|
63
|
+
console.log("");
|
|
64
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI configuration loader
|
|
3
|
+
* Reads reka.config.yaml → env vars → defaults
|
|
4
|
+
*/
|
|
5
|
+
export interface RekaConfig {
|
|
6
|
+
api: {
|
|
7
|
+
url: string;
|
|
8
|
+
key?: string;
|
|
9
|
+
};
|
|
10
|
+
project: {
|
|
11
|
+
name: string;
|
|
12
|
+
path: string;
|
|
13
|
+
};
|
|
14
|
+
models?: {
|
|
15
|
+
embeddings?: {
|
|
16
|
+
provider: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
dimensions?: number;
|
|
20
|
+
};
|
|
21
|
+
llm?: {
|
|
22
|
+
utility?: {
|
|
23
|
+
provider: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
};
|
|
27
|
+
standard?: {
|
|
28
|
+
provider: string;
|
|
29
|
+
model?: string;
|
|
30
|
+
url?: string;
|
|
31
|
+
};
|
|
32
|
+
complex?: {
|
|
33
|
+
provider: string;
|
|
34
|
+
model?: string;
|
|
35
|
+
url?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export declare function loadConfig(overrides?: Partial<RekaConfig>): RekaConfig;
|
|
41
|
+
export declare function getConfigFilePath(): string | null;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI configuration loader
|
|
4
|
+
* Reads reka.config.yaml → env vars → defaults
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.loadConfig = loadConfig;
|
|
41
|
+
exports.getConfigFilePath = getConfigFilePath;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const yaml = __importStar(require("js-yaml"));
|
|
45
|
+
const CONFIG_FILES = ["reka.config.yaml", "reka.config.yml", ".rekarc.yaml"];
|
|
46
|
+
function findConfigFile(startDir) {
|
|
47
|
+
let dir = startDir;
|
|
48
|
+
while (true) {
|
|
49
|
+
for (const name of CONFIG_FILES) {
|
|
50
|
+
const filePath = path.join(dir, name);
|
|
51
|
+
if (fs.existsSync(filePath))
|
|
52
|
+
return filePath;
|
|
53
|
+
}
|
|
54
|
+
const parent = path.dirname(dir);
|
|
55
|
+
if (parent === dir)
|
|
56
|
+
break;
|
|
57
|
+
dir = parent;
|
|
58
|
+
}
|
|
59
|
+
// Check home directory
|
|
60
|
+
const homeConfig = path.join(process.env.HOME || "~", ".config", "reka", "config.yaml");
|
|
61
|
+
if (fs.existsSync(homeConfig))
|
|
62
|
+
return homeConfig;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function loadConfig(overrides) {
|
|
66
|
+
const configFile = findConfigFile(process.cwd());
|
|
67
|
+
let fileConfig = {};
|
|
68
|
+
if (configFile) {
|
|
69
|
+
try {
|
|
70
|
+
const raw = fs.readFileSync(configFile, "utf-8");
|
|
71
|
+
fileConfig = yaml.load(raw) || {};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Silently ignore malformed config
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const projectName = overrides?.project?.name ||
|
|
78
|
+
fileConfig.project?.name ||
|
|
79
|
+
process.env.REKA_PROJECT ||
|
|
80
|
+
path.basename(process.cwd());
|
|
81
|
+
const config = {
|
|
82
|
+
api: {
|
|
83
|
+
url: overrides?.api?.url ||
|
|
84
|
+
fileConfig.api?.url ||
|
|
85
|
+
process.env.REKA_API_URL ||
|
|
86
|
+
process.env.RAG_API_URL ||
|
|
87
|
+
"http://localhost:3100",
|
|
88
|
+
key: overrides?.api?.key ||
|
|
89
|
+
fileConfig.api?.key ||
|
|
90
|
+
process.env.REKA_API_KEY ||
|
|
91
|
+
process.env.RAG_API_KEY,
|
|
92
|
+
},
|
|
93
|
+
project: {
|
|
94
|
+
name: projectName,
|
|
95
|
+
path: overrides?.project?.path ||
|
|
96
|
+
fileConfig.project?.path ||
|
|
97
|
+
process.env.REKA_PROJECT_PATH ||
|
|
98
|
+
process.cwd(),
|
|
99
|
+
},
|
|
100
|
+
models: fileConfig.models || overrides?.models,
|
|
101
|
+
};
|
|
102
|
+
return config;
|
|
103
|
+
}
|
|
104
|
+
function getConfigFilePath() {
|
|
105
|
+
return findConfigFile(process.cwd());
|
|
106
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Reka CLI — manage your self-hosted RAG infrastructure
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const config_1 = require("./config");
|
|
9
|
+
const api_1 = require("./api");
|
|
10
|
+
const status_1 = require("./commands/status");
|
|
11
|
+
const init_1 = require("./commands/init");
|
|
12
|
+
const index_1 = require("./commands/index");
|
|
13
|
+
const search_1 = require("./commands/search");
|
|
14
|
+
const models_1 = require("./commands/models");
|
|
15
|
+
const program = new commander_1.Command();
|
|
16
|
+
program
|
|
17
|
+
.name("reka")
|
|
18
|
+
.description("Reka — Memory your AI can trust")
|
|
19
|
+
.version("0.1.0")
|
|
20
|
+
.option("--api-url <url>", "RAG API URL")
|
|
21
|
+
.option("--api-key <key>", "API key")
|
|
22
|
+
.option("--project <name>", "Project name");
|
|
23
|
+
// reka init
|
|
24
|
+
program
|
|
25
|
+
.command("init")
|
|
26
|
+
.description("Initialize Reka for current project — generates API key and .mcp.json")
|
|
27
|
+
.option("--project <name>", "Project name (defaults to directory name)")
|
|
28
|
+
.option("-p, --path <path>", "Project path")
|
|
29
|
+
.option("-f, --force", "Overwrite existing .mcp.json")
|
|
30
|
+
.option("--cloud", "Connect to Reka Cloud instead of local")
|
|
31
|
+
.option("--key <key>", "API key (required for --cloud)")
|
|
32
|
+
.option("--api-url <url>", "RAG API URL (default: http://localhost:3100)")
|
|
33
|
+
.action(async (opts) => {
|
|
34
|
+
await (0, init_1.initCommand)(opts);
|
|
35
|
+
});
|
|
36
|
+
// reka status
|
|
37
|
+
program
|
|
38
|
+
.command("status")
|
|
39
|
+
.description("Show Reka API and project status")
|
|
40
|
+
.action(async () => {
|
|
41
|
+
const config = (0, config_1.loadConfig)(getOverrides());
|
|
42
|
+
const client = (0, api_1.createClient)(config);
|
|
43
|
+
await (0, status_1.statusCommand)(client, config);
|
|
44
|
+
});
|
|
45
|
+
// reka index [path]
|
|
46
|
+
program
|
|
47
|
+
.command("index [path]")
|
|
48
|
+
.description("Index a codebase for search")
|
|
49
|
+
.option("-w, --watch", "Watch for file changes")
|
|
50
|
+
.action(async (indexPath, opts) => {
|
|
51
|
+
const config = (0, config_1.loadConfig)(getOverrides());
|
|
52
|
+
const client = (0, api_1.createClient)(config);
|
|
53
|
+
await (0, index_1.indexCommand)(client, config, { path: indexPath, ...opts });
|
|
54
|
+
});
|
|
55
|
+
// reka search <query>
|
|
56
|
+
program
|
|
57
|
+
.command("search <query>")
|
|
58
|
+
.description("Search indexed codebase")
|
|
59
|
+
.option("-l, --limit <n>", "Number of results", "5")
|
|
60
|
+
.option("-t, --type <type>", "Collection type (codebase, docs, memory)")
|
|
61
|
+
.action(async (query, opts) => {
|
|
62
|
+
const config = (0, config_1.loadConfig)(getOverrides());
|
|
63
|
+
const client = (0, api_1.createClient)(config);
|
|
64
|
+
await (0, search_1.searchCommand)(client, config, query, opts);
|
|
65
|
+
});
|
|
66
|
+
// reka models
|
|
67
|
+
const models = program.command("models").description("Manage model providers");
|
|
68
|
+
models
|
|
69
|
+
.command("list")
|
|
70
|
+
.description("List configured model providers and their status")
|
|
71
|
+
.action(async () => {
|
|
72
|
+
const config = (0, config_1.loadConfig)(getOverrides());
|
|
73
|
+
await (0, models_1.modelsListCommand)(config);
|
|
74
|
+
});
|
|
75
|
+
models
|
|
76
|
+
.command("test")
|
|
77
|
+
.description("Test connection to all model providers")
|
|
78
|
+
.action(async () => {
|
|
79
|
+
const config = (0, config_1.loadConfig)(getOverrides());
|
|
80
|
+
await (0, models_1.modelsTestCommand)(config);
|
|
81
|
+
});
|
|
82
|
+
// Helper to extract global overrides
|
|
83
|
+
function getOverrides() {
|
|
84
|
+
const opts = program.opts();
|
|
85
|
+
const overrides = {};
|
|
86
|
+
if (opts.apiUrl || opts.apiKey) {
|
|
87
|
+
overrides.api = {};
|
|
88
|
+
if (opts.apiUrl)
|
|
89
|
+
overrides.api.url = opts.apiUrl;
|
|
90
|
+
if (opts.apiKey)
|
|
91
|
+
overrides.api.key = opts.apiKey;
|
|
92
|
+
}
|
|
93
|
+
if (opts.project) {
|
|
94
|
+
overrides.project = { name: opts.project };
|
|
95
|
+
}
|
|
96
|
+
return overrides;
|
|
97
|
+
}
|
|
98
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getreka/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Reka CLI — manage self-hosted RAG infrastructure for AI coding agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"reka": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"files": ["dist", "README.md"],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/getreka/reka.git",
|
|
16
|
+
"directory": "cli"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://getreka.dev",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"dev": "ts-node src/index.ts",
|
|
22
|
+
"start": "node dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"axios": "^1.7.0",
|
|
26
|
+
"chalk": "^4.1.2",
|
|
27
|
+
"commander": "^12.1.0",
|
|
28
|
+
"js-yaml": "^4.1.0",
|
|
29
|
+
"ora": "^5.4.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/js-yaml": "^4.0.9",
|
|
33
|
+
"@types/node": "^20.14.0",
|
|
34
|
+
"typescript": "^5.5.0",
|
|
35
|
+
"ts-node": "^10.9.2"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"license": "BSL-1.1",
|
|
41
|
+
"keywords": ["reka", "rag", "mcp", "ai", "cli", "coding-agent", "memory"]
|
|
42
|
+
}
|