@virsanghavi/axis-server 1.5.0 → 1.6.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/mcp-server.mjs +119 -49
- package/package.json +1 -1
package/dist/mcp-server.mjs
CHANGED
|
@@ -1166,6 +1166,7 @@ import fs4 from "fs";
|
|
|
1166
1166
|
|
|
1167
1167
|
// ../../src/local/local-search.ts
|
|
1168
1168
|
import fs3 from "fs/promises";
|
|
1169
|
+
import fsSync2 from "fs";
|
|
1169
1170
|
import path3 from "path";
|
|
1170
1171
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1171
1172
|
"node_modules",
|
|
@@ -1321,34 +1322,61 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
1321
1322
|
"most",
|
|
1322
1323
|
"some",
|
|
1323
1324
|
"any",
|
|
1324
|
-
"find",
|
|
1325
|
-
"show",
|
|
1326
|
-
"get",
|
|
1327
|
-
"look",
|
|
1328
|
-
"search",
|
|
1329
|
-
"locate",
|
|
1330
|
-
"check",
|
|
1331
|
-
"file",
|
|
1332
|
-
"files",
|
|
1333
|
-
"code",
|
|
1334
|
-
"function",
|
|
1335
|
-
"class",
|
|
1336
|
-
"method",
|
|
1337
1325
|
"there",
|
|
1338
1326
|
"here",
|
|
1339
1327
|
"just",
|
|
1340
1328
|
"also",
|
|
1341
1329
|
"very",
|
|
1342
1330
|
"really",
|
|
1343
|
-
"quite"
|
|
1331
|
+
"quite",
|
|
1332
|
+
"show",
|
|
1333
|
+
"look",
|
|
1334
|
+
"locate",
|
|
1335
|
+
"using",
|
|
1336
|
+
"used",
|
|
1337
|
+
"need",
|
|
1338
|
+
"want"
|
|
1344
1339
|
]);
|
|
1345
1340
|
var MAX_FILE_SIZE = 256 * 1024;
|
|
1346
1341
|
var MAX_RESULTS = 20;
|
|
1347
1342
|
var CONTEXT_LINES = 2;
|
|
1348
1343
|
var MAX_MATCHES_PER_FILE = 6;
|
|
1349
1344
|
function extractKeywords(query) {
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1345
|
+
const words = query.toLowerCase().replace(/[^\w\s\-_.]/g, " ").split(/\s+/).filter((w) => w.length >= 2);
|
|
1346
|
+
const filtered = words.filter((w) => !STOP_WORDS.has(w));
|
|
1347
|
+
const result = filtered.length > 0 ? filtered : words.filter((w) => w.length >= 3);
|
|
1348
|
+
return [...new Set(result)];
|
|
1349
|
+
}
|
|
1350
|
+
var PROJECT_ROOT_MARKERS = [
|
|
1351
|
+
".git",
|
|
1352
|
+
".axis",
|
|
1353
|
+
"package.json",
|
|
1354
|
+
"Cargo.toml",
|
|
1355
|
+
"go.mod",
|
|
1356
|
+
"pyproject.toml",
|
|
1357
|
+
"setup.py",
|
|
1358
|
+
"Gemfile",
|
|
1359
|
+
"pom.xml",
|
|
1360
|
+
"tsconfig.json",
|
|
1361
|
+
".cursorrules",
|
|
1362
|
+
"AGENTS.md"
|
|
1363
|
+
];
|
|
1364
|
+
function detectProjectRoot(startDir) {
|
|
1365
|
+
let current = startDir;
|
|
1366
|
+
const root = path3.parse(current).root;
|
|
1367
|
+
while (current !== root) {
|
|
1368
|
+
for (const marker of PROJECT_ROOT_MARKERS) {
|
|
1369
|
+
try {
|
|
1370
|
+
fsSync2.accessSync(path3.join(current, marker));
|
|
1371
|
+
return current;
|
|
1372
|
+
} catch {
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
const parent = path3.dirname(current);
|
|
1376
|
+
if (parent === current) break;
|
|
1377
|
+
current = parent;
|
|
1378
|
+
}
|
|
1379
|
+
return startDir;
|
|
1352
1380
|
}
|
|
1353
1381
|
async function walkDir(dir, maxDepth = 12) {
|
|
1354
1382
|
const results = [];
|
|
@@ -1428,8 +1456,12 @@ async function searchFile(filePath, rootDir, keywords) {
|
|
|
1428
1456
|
return { filePath, relativePath, score, matchedKeywords, regions };
|
|
1429
1457
|
}
|
|
1430
1458
|
async function localSearch(query, rootDir) {
|
|
1431
|
-
const
|
|
1459
|
+
const rawCwd = rootDir || process.cwd();
|
|
1460
|
+
const cwd = detectProjectRoot(rawCwd);
|
|
1432
1461
|
const keywords = extractKeywords(query);
|
|
1462
|
+
if (cwd !== rawCwd) {
|
|
1463
|
+
logger.info(`[localSearch] Detected project root: ${cwd} (CWD was: ${rawCwd})`);
|
|
1464
|
+
}
|
|
1433
1465
|
if (keywords.length === 0) {
|
|
1434
1466
|
return "Could not extract meaningful search terms from the query. Try being more specific (e.g. 'authentication middleware' instead of 'how does it work').";
|
|
1435
1467
|
}
|
|
@@ -1557,8 +1589,20 @@ var subscription = {
|
|
|
1557
1589
|
};
|
|
1558
1590
|
async function verifySubscription() {
|
|
1559
1591
|
if (!apiSecret) {
|
|
1560
|
-
|
|
1561
|
-
|
|
1592
|
+
const hasDirectSupabase = !useRemoteApiOnly && !!process.env.NEXT_PUBLIC_SUPABASE_URL && !!process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
1593
|
+
if (hasDirectSupabase) {
|
|
1594
|
+
subscription = { checked: true, valid: true, plan: "developer", reason: "Direct Supabase mode \u2014 no API key needed", checkedAt: Date.now() };
|
|
1595
|
+
logger.info("[subscription] Direct Supabase credentials found \u2014 developer mode, skipping verification");
|
|
1596
|
+
return subscription;
|
|
1597
|
+
}
|
|
1598
|
+
subscription = {
|
|
1599
|
+
checked: true,
|
|
1600
|
+
valid: false,
|
|
1601
|
+
plan: "none",
|
|
1602
|
+
reason: "no_api_key",
|
|
1603
|
+
checkedAt: Date.now()
|
|
1604
|
+
};
|
|
1605
|
+
logger.error("[subscription] No API key configured. Axis requires an API key from https://useaxis.dev/dashboard");
|
|
1562
1606
|
return subscription;
|
|
1563
1607
|
}
|
|
1564
1608
|
const verifyUrl = apiUrl.endsWith("/v1") ? `${apiUrl}/verify` : `${apiUrl}/v1/verify`;
|
|
@@ -1616,6 +1660,22 @@ function isSubscriptionStale() {
|
|
|
1616
1660
|
return Date.now() - subscription.checkedAt > RECHECK_INTERVAL_MS;
|
|
1617
1661
|
}
|
|
1618
1662
|
function getSubscriptionBlockMessage() {
|
|
1663
|
+
if (subscription.reason === "no_api_key") {
|
|
1664
|
+
return [
|
|
1665
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
1666
|
+
" Axis API key required",
|
|
1667
|
+
"",
|
|
1668
|
+
" No API key found. Axis requires an active subscription",
|
|
1669
|
+
" and a valid API key to operate.",
|
|
1670
|
+
"",
|
|
1671
|
+
" 1. Sign up or log in at https://useaxis.dev",
|
|
1672
|
+
" 2. Subscribe to Axis Pro",
|
|
1673
|
+
" 3. Generate an API key from the dashboard",
|
|
1674
|
+
" 4. Add AXIS_API_KEY to your mcp.json configuration",
|
|
1675
|
+
" 5. Restart your IDE",
|
|
1676
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
|
|
1677
|
+
].join("\n");
|
|
1678
|
+
}
|
|
1619
1679
|
return [
|
|
1620
1680
|
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
1621
1681
|
" Axis Pro subscription required",
|
|
@@ -1644,7 +1704,7 @@ async function ensureFileSystem() {
|
|
|
1644
1704
|
try {
|
|
1645
1705
|
const fs5 = await import("fs/promises");
|
|
1646
1706
|
const path5 = await import("path");
|
|
1647
|
-
const
|
|
1707
|
+
const fsSync3 = await import("fs");
|
|
1648
1708
|
const cwd = process.cwd();
|
|
1649
1709
|
logger.info(`Server CWD: ${cwd}`);
|
|
1650
1710
|
const historyDir = path5.join(cwd, "history");
|
|
@@ -1653,7 +1713,7 @@ async function ensureFileSystem() {
|
|
|
1653
1713
|
const axisDir = path5.join(cwd, ".axis");
|
|
1654
1714
|
const axisInstructions = path5.join(axisDir, "instructions");
|
|
1655
1715
|
const legacyInstructions = path5.join(cwd, "agent-instructions");
|
|
1656
|
-
if (
|
|
1716
|
+
if (fsSync3.existsSync(legacyInstructions) && !fsSync3.existsSync(axisDir)) {
|
|
1657
1717
|
logger.info("Using legacy agent-instructions directory");
|
|
1658
1718
|
} else {
|
|
1659
1719
|
await fs5.mkdir(axisInstructions, { recursive: true }).catch(() => {
|
|
@@ -2036,41 +2096,51 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2036
2096
|
}
|
|
2037
2097
|
if (name === SEARCH_CONTEXT_TOOL) {
|
|
2038
2098
|
const query = String(args?.query);
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
const remote = await manager.searchContext(query, nerveCenter.currentProjectName);
|
|
2042
|
-
if (remote && !remote.includes("No results found") && remote.trim().length > 20) {
|
|
2043
|
-
ragResults = remote;
|
|
2044
|
-
}
|
|
2045
|
-
} catch {
|
|
2046
|
-
}
|
|
2047
|
-
if (!ragResults && ragEngine) {
|
|
2048
|
-
try {
|
|
2049
|
-
const localRag = await ragEngine.search(query);
|
|
2050
|
-
if (localRag.length > 0) {
|
|
2051
|
-
ragResults = localRag.join("\n---\n");
|
|
2052
|
-
}
|
|
2053
|
-
} catch {
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
let localResults = null;
|
|
2099
|
+
logger.info(`[search_codebase] Query: "${query}"`);
|
|
2100
|
+
let localResults = "";
|
|
2057
2101
|
try {
|
|
2058
2102
|
localResults = await localSearch(query);
|
|
2103
|
+
logger.info(`[search_codebase] Local search completed: ${localResults.length} chars`);
|
|
2059
2104
|
} catch (e) {
|
|
2060
2105
|
logger.warn(`[search_codebase] Local search error: ${e}`);
|
|
2106
|
+
localResults = "";
|
|
2061
2107
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2108
|
+
let ragResults = null;
|
|
2109
|
+
const RAG_TIMEOUT_MS = 3e3;
|
|
2110
|
+
try {
|
|
2111
|
+
const ragPromise = (async () => {
|
|
2112
|
+
try {
|
|
2113
|
+
const remote = await manager.searchContext(query, nerveCenter.currentProjectName);
|
|
2114
|
+
if (remote && !remote.includes("No results found") && remote.trim().length > 20) {
|
|
2115
|
+
return remote;
|
|
2116
|
+
}
|
|
2117
|
+
} catch {
|
|
2118
|
+
}
|
|
2119
|
+
if (ragEngine) {
|
|
2120
|
+
try {
|
|
2121
|
+
const results = await ragEngine.search(query);
|
|
2122
|
+
if (results.length > 0) return results.join("\n---\n");
|
|
2123
|
+
} catch {
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
return null;
|
|
2127
|
+
})();
|
|
2128
|
+
ragResults = await Promise.race([
|
|
2129
|
+
ragPromise,
|
|
2130
|
+
new Promise((resolve) => setTimeout(() => resolve(null), RAG_TIMEOUT_MS))
|
|
2131
|
+
]);
|
|
2132
|
+
if (ragResults) {
|
|
2133
|
+
logger.info(`[search_codebase] RAG returned results (${ragResults.length} chars)`);
|
|
2134
|
+
}
|
|
2135
|
+
} catch {
|
|
2070
2136
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2137
|
+
const hasLocal = localResults && !localResults.startsWith("No matches found") && !localResults.startsWith("Could not extract");
|
|
2138
|
+
if (!hasLocal && !ragResults) {
|
|
2139
|
+
return { content: [{ type: "text", text: localResults || "No results found for this query." }] };
|
|
2073
2140
|
}
|
|
2141
|
+
const parts = [];
|
|
2142
|
+
if (hasLocal) parts.push(localResults);
|
|
2143
|
+
if (ragResults) parts.push("## Indexed Results (RAG)\n\n" + ragResults);
|
|
2074
2144
|
return { content: [{ type: "text", text: parts.join("\n\n---\n\n") }] };
|
|
2075
2145
|
}
|
|
2076
2146
|
if (name === "get_subscription_status") {
|