@neurcode-ai/cli 0.8.0 → 0.8.2

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,239 @@
1
+ "use strict";
2
+ /**
3
+ * RelevanceScorer
4
+ *
5
+ * High-performance BM25-inspired keyword matching algorithm for filtering
6
+ * exported tools based on user intent. Runs in milliseconds without requiring
7
+ * vector databases or embeddings.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.getTopKTools = getTopKTools;
11
+ // Common stop words to filter from intent
12
+ const STOP_WORDS = new Set([
13
+ 'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',
14
+ 'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'that', 'the',
15
+ 'to', 'was', 'will', 'with', 'the', 'this', 'but', 'they', 'have',
16
+ 'had', 'what', 'said', 'each', 'which', 'their', 'time', 'if',
17
+ 'up', 'out', 'many', 'then', 'them', 'these', 'so', 'some', 'her',
18
+ 'would', 'make', 'like', 'into', 'him', 'has', 'two', 'more', 'very',
19
+ 'after', 'words', 'long', 'than', 'first', 'been', 'call', 'who',
20
+ 'oil', 'sit', 'now', 'find', 'down', 'day', 'did', 'get', 'come',
21
+ 'made', 'may', 'part'
22
+ ]);
23
+ /**
24
+ * Clean and tokenize intent text
25
+ */
26
+ function tokenizeIntent(intent) {
27
+ return intent
28
+ .toLowerCase()
29
+ .replace(/[^\w\s]/g, ' ') // Remove punctuation
30
+ .split(/\s+/)
31
+ .filter(word => word.length > 2 && !STOP_WORDS.has(word));
32
+ }
33
+ /**
34
+ * Extract folder names from file path
35
+ * e.g., "src/services/auth/validator.ts" -> ["src", "services", "auth"]
36
+ */
37
+ function extractFolderNames(filePath) {
38
+ const parts = filePath.split('/');
39
+ return parts.slice(0, -1); // Remove filename, keep folders
40
+ }
41
+ /**
42
+ * Check if a word appears in export name (case-insensitive)
43
+ */
44
+ function fuzzyMatch(word, exportName) {
45
+ const lowerExport = exportName.toLowerCase();
46
+ const lowerWord = word.toLowerCase();
47
+ // Direct substring match
48
+ if (lowerExport.includes(lowerWord))
49
+ return true;
50
+ // CamelCase split match (e.g., "validateAuth" matches "auth")
51
+ const camelCaseParts = exportName.split(/(?=[A-Z])/).map(p => p.toLowerCase());
52
+ return camelCaseParts.some(part => part.includes(lowerWord));
53
+ }
54
+ /**
55
+ * Calculate relevance score for an export item
56
+ */
57
+ function calculateScore(exportItem, intentTokens) {
58
+ let score = 0;
59
+ const exportName = exportItem.name.toLowerCase();
60
+ const filePath = exportItem.filePath.toLowerCase();
61
+ // Direct Match: +10 if export name appears exactly in intent
62
+ for (const token of intentTokens) {
63
+ if (exportName === token) {
64
+ score += 10;
65
+ break; // Only count once
66
+ }
67
+ }
68
+ // Fuzzy Match: +5 if a word in intent matches part of export name
69
+ for (const token of intentTokens) {
70
+ if (fuzzyMatch(token, exportItem.name)) {
71
+ score += 5;
72
+ break; // Only count once per token
73
+ }
74
+ }
75
+ // Path Match: +3 if folder name matches intent
76
+ const folderNames = extractFolderNames(exportItem.filePath);
77
+ for (const folder of folderNames) {
78
+ const lowerFolder = folder.toLowerCase();
79
+ for (const token of intentTokens) {
80
+ if (lowerFolder.includes(token) || token.includes(lowerFolder)) {
81
+ score += 3;
82
+ break; // Only count once per folder
83
+ }
84
+ }
85
+ }
86
+ // Type Weighting: Functions > Classes > Interfaces/Types
87
+ switch (exportItem.type) {
88
+ case 'function':
89
+ score += 2; // Functions are most actionable
90
+ break;
91
+ case 'class':
92
+ score += 1; // Classes are actionable
93
+ break;
94
+ case 'interface':
95
+ case 'type':
96
+ score += 0; // Interfaces/types are less actionable
97
+ break;
98
+ case 'const':
99
+ case 'variable':
100
+ score += 0.5; // Constants can be useful
101
+ break;
102
+ default:
103
+ score += 0;
104
+ }
105
+ return score;
106
+ }
107
+ /**
108
+ * Core Guard: Always include these essential tools regardless of relevance
109
+ */
110
+ const CORE_TOOLS = new Set([
111
+ 'plan',
112
+ 'verify',
113
+ 'apply',
114
+ 'check',
115
+ 'generate',
116
+ 'create',
117
+ 'update',
118
+ 'delete',
119
+ 'save',
120
+ 'load',
121
+ 'init',
122
+ 'config',
123
+ 'help'
124
+ ]);
125
+ /**
126
+ * Check if an export is a core tool that should always be included
127
+ */
128
+ function isCoreTool(exportItem) {
129
+ const name = exportItem.name.toLowerCase();
130
+ // Check exact match
131
+ if (CORE_TOOLS.has(name))
132
+ return true;
133
+ // Check if name contains core tool (e.g., "generatePlan" contains "plan")
134
+ for (const coreTool of CORE_TOOLS) {
135
+ if (name.includes(coreTool) || coreTool.includes(name)) {
136
+ return true;
137
+ }
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Get Top-K most relevant tools based on user intent
143
+ *
144
+ * @param intent - User's intent/query
145
+ * @param exports - All available exports
146
+ * @param k - Number of top results to return (default: 20)
147
+ * @returns Filtered and sorted array of ExportItems
148
+ */
149
+ function getTopKTools(intent, exports, k = 20) {
150
+ if (!intent || intent.trim().length === 0) {
151
+ // If no intent, return top K by type priority (functions first)
152
+ return exports
153
+ .sort((a, b) => {
154
+ const typePriority = {
155
+ 'function': 3,
156
+ 'class': 2,
157
+ 'const': 1,
158
+ 'interface': 0,
159
+ 'type': 0,
160
+ 'variable': 0,
161
+ 'enum': 0,
162
+ 'namespace': 0,
163
+ 'default': 0,
164
+ 'unknown': 0
165
+ };
166
+ return (typePriority[b.type] || 0) - (typePriority[a.type] || 0);
167
+ })
168
+ .slice(0, k);
169
+ }
170
+ // Tokenize intent
171
+ const intentTokens = tokenizeIntent(intent);
172
+ if (intentTokens.length === 0) {
173
+ // If intent has no meaningful tokens, return top K by type
174
+ return exports
175
+ .sort((a, b) => {
176
+ const typePriority = {
177
+ 'function': 3,
178
+ 'class': 2,
179
+ 'const': 1,
180
+ 'interface': 0,
181
+ 'type': 0
182
+ };
183
+ return (typePriority[b.type] || 0) - (typePriority[a.type] || 0);
184
+ })
185
+ .slice(0, k);
186
+ }
187
+ // Score all exports
188
+ const scoredExports = exports.map(exp => ({
189
+ export: exp,
190
+ score: calculateScore(exp, intentTokens),
191
+ isCore: isCoreTool(exp)
192
+ }));
193
+ // Separate core tools and regular exports
194
+ const coreTools = scoredExports.filter(item => item.isCore);
195
+ const regularExports = scoredExports.filter(item => !item.isCore);
196
+ // Sort regular exports by score (descending)
197
+ regularExports.sort((a, b) => {
198
+ if (b.score !== a.score) {
199
+ return b.score - a.score;
200
+ }
201
+ // Tie-breaker: prefer functions
202
+ const typePriority = {
203
+ 'function': 3,
204
+ 'class': 2,
205
+ 'const': 1,
206
+ 'interface': 0,
207
+ 'type': 0
208
+ };
209
+ return (typePriority[b.export.type] || 0) - (typePriority[a.export.type] || 0);
210
+ });
211
+ // Combine: core tools first, then top-scoring regular exports
212
+ const coreToolItems = coreTools.map(item => item.export);
213
+ const topRegularExports = regularExports
214
+ .slice(0, Math.max(0, k - coreToolItems.length))
215
+ .map(item => item.export);
216
+ // Remove duplicates (in case a core tool also scored high)
217
+ const seen = new Set();
218
+ const result = [];
219
+ for (const exp of [...coreToolItems, ...topRegularExports]) {
220
+ const key = `${exp.filePath}:${exp.name}`;
221
+ if (!seen.has(key)) {
222
+ seen.add(key);
223
+ result.push(exp);
224
+ }
225
+ }
226
+ // If we still need more items, fill with highest-scoring remaining
227
+ if (result.length < k) {
228
+ const remaining = regularExports
229
+ .filter(item => {
230
+ const key = `${item.export.filePath}:${item.export.name}`;
231
+ return !seen.has(key);
232
+ })
233
+ .slice(0, k - result.length)
234
+ .map(item => item.export);
235
+ result.push(...remaining);
236
+ }
237
+ return result.slice(0, k);
238
+ }
239
+ //# sourceMappingURL=RelevanceScorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelevanceScorer.js","sourceRoot":"","sources":["../../src/utils/RelevanceScorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tier Utility
3
+ *
4
+ * Fetches and caches user tier to minimize API latency.
5
+ * Caches tier in .neurcode/config.json for the session.
6
+ */
7
+ export type UserTier = 'FREE' | 'PRO';
8
+ /**
9
+ * Get user tier from API or cache
10
+ * Defaults to 'FREE' if tier cannot be determined
11
+ */
12
+ export declare function getUserTier(): Promise<UserTier>;
13
+ /**
14
+ * Clear tier cache (useful after tier changes)
15
+ */
16
+ export declare function clearTierCache(): void;
17
+ /**
18
+ * Check if user has PRO tier
19
+ */
20
+ export declare function isProUser(): Promise<boolean>;
21
+ //# sourceMappingURL=tier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier.d.ts","sourceRoot":"","sources":["../../src/utils/tier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAUtC;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,CA+ErD;AAwCD;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAiBrC;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAGlD"}
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ /**
3
+ * Tier Utility
4
+ *
5
+ * Fetches and caches user tier to minimize API latency.
6
+ * Caches tier in .neurcode/config.json for the session.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getUserTier = getUserTier;
10
+ exports.clearTierCache = clearTierCache;
11
+ exports.isProUser = isProUser;
12
+ const fs_1 = require("fs");
13
+ const path_1 = require("path");
14
+ const config_1 = require("../config");
15
+ const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
16
+ let memoryCache = null;
17
+ /**
18
+ * Get user tier from API or cache
19
+ * Defaults to 'FREE' if tier cannot be determined
20
+ */
21
+ async function getUserTier() {
22
+ try {
23
+ // Check memory cache first
24
+ const now = Date.now();
25
+ if (memoryCache && (now - memoryCache.cachedAt) < CACHE_TTL) {
26
+ return memoryCache.tier;
27
+ }
28
+ // Check file cache
29
+ const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
30
+ const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
31
+ if ((0, fs_1.existsSync)(configPath)) {
32
+ try {
33
+ const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
34
+ const config = JSON.parse(fileContent);
35
+ if (config.tier && config.tierCachedAt) {
36
+ const cacheAge = now - config.tierCachedAt;
37
+ if (cacheAge < CACHE_TTL) {
38
+ const tier = config.tier;
39
+ memoryCache = { tier, cachedAt: config.tierCachedAt };
40
+ return tier;
41
+ }
42
+ }
43
+ }
44
+ catch (error) {
45
+ // Ignore parse errors, continue to API fetch
46
+ }
47
+ }
48
+ // Fetch from API
49
+ const config = (0, config_1.loadConfig)();
50
+ if (!config.apiKey) {
51
+ // No API key, default to FREE
52
+ return 'FREE';
53
+ }
54
+ const apiUrl = (config.apiUrl || 'https://api.neurcode.com').replace(/\/$/, '');
55
+ const apiKey = config.apiKey;
56
+ // Try to get tier from subscription endpoint
57
+ try {
58
+ const response = await fetch(`${apiUrl}/api/v1/subscriptions/status`, {
59
+ method: 'GET',
60
+ headers: {
61
+ 'Authorization': `Bearer ${apiKey}`,
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ });
65
+ if (!response.ok) {
66
+ // If not authorized or subscription not found, default to FREE
67
+ return 'FREE';
68
+ }
69
+ const subscription = await response.json();
70
+ // PRO tier if: professional plan AND (active status OR trial status)
71
+ const tier = subscription.plan.slug === 'professional' &&
72
+ (subscription.status === 'active' || subscription.status === 'trial' || subscription.isTrial)
73
+ ? 'PRO' : 'FREE';
74
+ // Cache the tier
75
+ cacheTier(tier);
76
+ return tier;
77
+ }
78
+ catch (error) {
79
+ // If subscription endpoint fails, default to FREE
80
+ // Don't log warning to avoid noise - this is expected for FREE users
81
+ return 'FREE';
82
+ }
83
+ }
84
+ catch (error) {
85
+ // Fail-safe: default to FREE
86
+ return 'FREE';
87
+ }
88
+ }
89
+ /**
90
+ * Cache tier in memory and file
91
+ */
92
+ function cacheTier(tier) {
93
+ const now = Date.now();
94
+ memoryCache = { tier, cachedAt: now };
95
+ try {
96
+ const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
97
+ const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
98
+ // Ensure .neurcode directory exists
99
+ if (!(0, fs_1.existsSync)(neurcodeDir)) {
100
+ (0, fs_1.mkdirSync)(neurcodeDir, { recursive: true });
101
+ }
102
+ // Read existing config or create new
103
+ let config = {};
104
+ if ((0, fs_1.existsSync)(configPath)) {
105
+ try {
106
+ const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
107
+ config = JSON.parse(fileContent);
108
+ }
109
+ catch (error) {
110
+ // If parse fails, start with empty config
111
+ }
112
+ }
113
+ // Update tier cache
114
+ config.tier = tier;
115
+ config.tierCachedAt = now;
116
+ // Write back to file
117
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), 'utf-8');
118
+ }
119
+ catch (error) {
120
+ // Ignore file write errors - memory cache is still valid
121
+ }
122
+ }
123
+ /**
124
+ * Clear tier cache (useful after tier changes)
125
+ */
126
+ function clearTierCache() {
127
+ memoryCache = null;
128
+ try {
129
+ const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
130
+ const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
131
+ if ((0, fs_1.existsSync)(configPath)) {
132
+ const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
133
+ const config = JSON.parse(fileContent);
134
+ delete config.tier;
135
+ delete config.tierCachedAt;
136
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), 'utf-8');
137
+ }
138
+ }
139
+ catch (error) {
140
+ // Ignore errors
141
+ }
142
+ }
143
+ /**
144
+ * Check if user has PRO tier
145
+ */
146
+ async function isProUser() {
147
+ const tier = await getUserTier();
148
+ return tier === 'PRO';
149
+ }
150
+ //# sourceMappingURL=tier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier.js","sourceRoot":"","sources":["../../src/utils/tier.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurcode-ai/cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Neurcode CLI - AI code governance and diff analysis",
5
5
  "bin": {
6
6
  "neurcode": "./dist/index.js"