archicore 0.1.8 → 0.2.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/cli/commands/auth.d.ts +4 -0
- package/dist/cli/commands/auth.js +58 -0
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.js +2 -1
- package/dist/cli/commands/interactive.js +107 -59
- package/dist/cli/ui/index.d.ts +1 -0
- package/dist/cli/ui/index.js +1 -0
- package/dist/cli/ui/progress.d.ts +57 -0
- package/dist/cli/ui/progress.js +149 -0
- package/dist/cli/utils/error-handler.d.ts +40 -0
- package/dist/cli/utils/error-handler.js +234 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.js +3 -0
- package/dist/cli/utils/project-selector.d.ts +33 -0
- package/dist/cli/utils/project-selector.js +148 -0
- package/dist/cli.js +3 -1
- package/dist/code-index/ast-parser.d.ts +1 -1
- package/dist/code-index/ast-parser.js +9 -3
- package/dist/code-index/index.d.ts +1 -1
- package/dist/code-index/index.js +2 -2
- package/dist/github/github-service.js +8 -1
- package/dist/orchestrator/index.js +29 -1
- package/dist/semantic-memory/embedding-service.d.ts +4 -1
- package/dist/semantic-memory/embedding-service.js +58 -11
- package/dist/semantic-memory/index.d.ts +1 -1
- package/dist/semantic-memory/index.js +54 -7
- package/dist/server/config.d.ts +52 -0
- package/dist/server/config.js +88 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +115 -10
- package/dist/server/routes/admin.js +3 -3
- package/dist/server/routes/api.js +199 -2
- package/dist/server/routes/auth.js +79 -5
- package/dist/server/routes/device-auth.js +1 -1
- package/dist/server/routes/github.js +33 -0
- package/dist/server/services/auth-service.d.ts +5 -0
- package/dist/server/services/auth-service.js +10 -0
- package/dist/server/services/project-service.d.ts +15 -1
- package/dist/server/services/project-service.js +185 -26
- package/dist/types/user.d.ts +2 -2
- package/dist/types/user.js +13 -4
- package/package.json +9 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArchiCore CLI - Error Handler
|
|
3
|
+
*
|
|
4
|
+
* User-friendly error messages with suggestions
|
|
5
|
+
*/
|
|
6
|
+
import { colors, icons } from '../ui/colors.js';
|
|
7
|
+
const KNOWN_ERRORS = [
|
|
8
|
+
// Network errors
|
|
9
|
+
{
|
|
10
|
+
pattern: /ECONNREFUSED|Failed to fetch|NetworkError/i,
|
|
11
|
+
title: 'Connection refused',
|
|
12
|
+
suggestion: 'Make sure the ArchiCore server is running',
|
|
13
|
+
details: 'Run `npm run server` or check if the server is accessible',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
pattern: /ETIMEDOUT|timeout/i,
|
|
17
|
+
title: 'Connection timeout',
|
|
18
|
+
suggestion: 'Check your network connection or server status',
|
|
19
|
+
details: 'The server might be overloaded or unreachable',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /ENOTFOUND|DNS/i,
|
|
23
|
+
title: 'Server not found',
|
|
24
|
+
suggestion: 'Check the server URL in your configuration',
|
|
25
|
+
details: 'Run `archicore config` to update server settings',
|
|
26
|
+
},
|
|
27
|
+
// Auth errors
|
|
28
|
+
{
|
|
29
|
+
pattern: /401|Unauthorized|Invalid token/i,
|
|
30
|
+
title: 'Authentication failed',
|
|
31
|
+
suggestion: 'Your session may have expired',
|
|
32
|
+
details: 'Run `archicore login` to authenticate again',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /403|Forbidden|Access denied/i,
|
|
36
|
+
title: 'Access denied',
|
|
37
|
+
suggestion: 'You don\'t have permission for this operation',
|
|
38
|
+
details: 'Check your subscription or contact support',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
pattern: /429|Too many requests|rate limit/i,
|
|
42
|
+
title: 'Rate limit exceeded',
|
|
43
|
+
suggestion: 'Wait a moment before trying again',
|
|
44
|
+
details: 'Upgrade your plan for higher limits',
|
|
45
|
+
},
|
|
46
|
+
// Project errors
|
|
47
|
+
{
|
|
48
|
+
pattern: /Project not found|No project/i,
|
|
49
|
+
title: 'Project not found',
|
|
50
|
+
suggestion: 'Make sure the project exists and is indexed',
|
|
51
|
+
details: 'Run `archicore init` in your project directory',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /Not indexed|Index required/i,
|
|
55
|
+
title: 'Project not indexed',
|
|
56
|
+
suggestion: 'Index your project first',
|
|
57
|
+
details: 'Run `/index` command in interactive mode',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
pattern: /No files found|Empty project/i,
|
|
61
|
+
title: 'No files found',
|
|
62
|
+
suggestion: 'Check that your project has source files',
|
|
63
|
+
details: 'Make sure .ts, .js, or .py files exist in the directory',
|
|
64
|
+
},
|
|
65
|
+
// Qdrant/Vector DB errors
|
|
66
|
+
{
|
|
67
|
+
pattern: /Qdrant|vector.*error|embedding/i,
|
|
68
|
+
title: 'Vector database error',
|
|
69
|
+
suggestion: 'Check if Qdrant is running',
|
|
70
|
+
details: 'Run `docker-compose up -d qdrant` or check Qdrant status',
|
|
71
|
+
},
|
|
72
|
+
// LLM/API errors
|
|
73
|
+
{
|
|
74
|
+
pattern: /OpenAI|Anthropic|DeepSeek|API key/i,
|
|
75
|
+
title: 'AI provider error',
|
|
76
|
+
suggestion: 'Check your API keys in .env file',
|
|
77
|
+
details: 'Make sure OPENAI_API_KEY, ANTHROPIC_API_KEY, or DEEPSEEK_API_KEY is set',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
pattern: /quota|credits|billing/i,
|
|
81
|
+
title: 'API quota exceeded',
|
|
82
|
+
suggestion: 'Check your AI provider billing',
|
|
83
|
+
details: 'You may have run out of credits on OpenAI/Anthropic/DeepSeek',
|
|
84
|
+
},
|
|
85
|
+
// File errors
|
|
86
|
+
{
|
|
87
|
+
pattern: /ENOENT|file not found|no such file/i,
|
|
88
|
+
title: 'File not found',
|
|
89
|
+
suggestion: 'Check the file path',
|
|
90
|
+
details: 'Make sure the file exists and is accessible',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
pattern: /EACCES|permission denied/i,
|
|
94
|
+
title: 'Permission denied',
|
|
95
|
+
suggestion: 'Check file permissions',
|
|
96
|
+
details: 'You may need to run with elevated privileges',
|
|
97
|
+
},
|
|
98
|
+
// Parse errors
|
|
99
|
+
{
|
|
100
|
+
pattern: /Syntax error|Parse error|Invalid JSON/i,
|
|
101
|
+
title: 'Parse error',
|
|
102
|
+
suggestion: 'Check the file for syntax errors',
|
|
103
|
+
details: 'Run your linter or IDE to find syntax issues',
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
/**
|
|
107
|
+
* Format error for CLI display
|
|
108
|
+
*/
|
|
109
|
+
export function formatError(error, context) {
|
|
110
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
111
|
+
const statusCode = context?.statusCode;
|
|
112
|
+
// Find matching known error
|
|
113
|
+
const knownError = KNOWN_ERRORS.find(ke => {
|
|
114
|
+
if (typeof ke.pattern === 'string') {
|
|
115
|
+
return errorMessage.toLowerCase().includes(ke.pattern.toLowerCase());
|
|
116
|
+
}
|
|
117
|
+
return ke.pattern.test(errorMessage);
|
|
118
|
+
});
|
|
119
|
+
const lines = [];
|
|
120
|
+
// Title line
|
|
121
|
+
const title = knownError?.title || context?.operation || 'Error';
|
|
122
|
+
lines.push(colors.error(` ${icons.error} ${title}`));
|
|
123
|
+
// Error message
|
|
124
|
+
if (knownError) {
|
|
125
|
+
lines.push(colors.muted(` ${errorMessage}`));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lines.push(` ${errorMessage}`);
|
|
129
|
+
}
|
|
130
|
+
// Status code if available
|
|
131
|
+
if (statusCode && statusCode >= 400) {
|
|
132
|
+
lines.push(colors.dim(` HTTP ${statusCode}`));
|
|
133
|
+
}
|
|
134
|
+
// Suggestion
|
|
135
|
+
const suggestion = context?.suggestion || knownError?.suggestion;
|
|
136
|
+
if (suggestion) {
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push(colors.info(` ${icons.info} ${suggestion}`));
|
|
139
|
+
}
|
|
140
|
+
// Details
|
|
141
|
+
const details = context?.details || knownError?.details;
|
|
142
|
+
if (details) {
|
|
143
|
+
lines.push(colors.dim(` ${details}`));
|
|
144
|
+
}
|
|
145
|
+
return lines.join('\n');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Print error with formatting
|
|
149
|
+
*/
|
|
150
|
+
export function printFormattedError(error, context) {
|
|
151
|
+
console.log();
|
|
152
|
+
console.log(formatError(error, context));
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Wrap async operation with error handling
|
|
157
|
+
*/
|
|
158
|
+
export async function withErrorHandler(operation, context) {
|
|
159
|
+
try {
|
|
160
|
+
return await operation();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
printFormattedError(error, context);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create contextual error handler
|
|
169
|
+
*/
|
|
170
|
+
export function createErrorHandler(defaultContext) {
|
|
171
|
+
return {
|
|
172
|
+
handle: (error, additionalContext) => {
|
|
173
|
+
printFormattedError(error, { ...defaultContext, ...additionalContext });
|
|
174
|
+
},
|
|
175
|
+
wrap: async (operation, additionalContext) => {
|
|
176
|
+
return withErrorHandler(operation, { ...defaultContext, ...additionalContext });
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* HTTP response error handler
|
|
182
|
+
*/
|
|
183
|
+
export async function handleResponseError(response, operation) {
|
|
184
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
185
|
+
try {
|
|
186
|
+
const data = await response.json();
|
|
187
|
+
errorMessage = data.error || data.message || errorMessage;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Use status text if JSON parsing fails
|
|
191
|
+
errorMessage = response.statusText || errorMessage;
|
|
192
|
+
}
|
|
193
|
+
const error = new Error(errorMessage);
|
|
194
|
+
printFormattedError(error, {
|
|
195
|
+
statusCode: response.status,
|
|
196
|
+
operation,
|
|
197
|
+
});
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Print startup errors nicely
|
|
202
|
+
*/
|
|
203
|
+
export function printStartupError(error) {
|
|
204
|
+
console.log();
|
|
205
|
+
console.log(colors.error.bold(' ArchiCore failed to start'));
|
|
206
|
+
console.log();
|
|
207
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
208
|
+
// Check for common startup issues
|
|
209
|
+
if (message.includes('ECONNREFUSED') || message.includes('Cannot connect')) {
|
|
210
|
+
console.log(colors.warning(` ${icons.warning} Server connection failed`));
|
|
211
|
+
console.log();
|
|
212
|
+
console.log(colors.muted(' Possible solutions:'));
|
|
213
|
+
console.log(colors.dim(' 1. Start the server: npm run server'));
|
|
214
|
+
console.log(colors.dim(' 2. Check server URL in .archicore/config.json'));
|
|
215
|
+
console.log(colors.dim(' 3. Make sure port 3000 is not blocked'));
|
|
216
|
+
}
|
|
217
|
+
else if (message.includes('not initialized')) {
|
|
218
|
+
console.log(colors.warning(` ${icons.warning} Project not initialized`));
|
|
219
|
+
console.log();
|
|
220
|
+
console.log(colors.muted(' Run this command to initialize:'));
|
|
221
|
+
console.log(colors.primary(' archicore init'));
|
|
222
|
+
}
|
|
223
|
+
else if (message.includes('Authentication') || message.includes('token')) {
|
|
224
|
+
console.log(colors.warning(` ${icons.warning} Authentication required`));
|
|
225
|
+
console.log();
|
|
226
|
+
console.log(colors.muted(' Run this command to login:'));
|
|
227
|
+
console.log(colors.primary(' archicore login'));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(colors.muted(` ${message}`));
|
|
231
|
+
}
|
|
232
|
+
console.log();
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -3,4 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export * from './config.js';
|
|
5
5
|
export * from './session.js';
|
|
6
|
+
export { fetchUserProjects, selectProject as selectProjectInteractive, quickProjectSwitch, displayProjectInfo, searchProjects, type ProjectInfo as ProjectSelectorInfo, } from './project-selector.js';
|
|
7
|
+
export * from './error-handler.js';
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cli/utils/index.js
CHANGED
|
@@ -3,4 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export * from './config.js';
|
|
5
5
|
export * from './session.js';
|
|
6
|
+
// project-selector has renamed exports to avoid conflicts with session.js
|
|
7
|
+
export { fetchUserProjects, selectProject as selectProjectInteractive, quickProjectSwitch, displayProjectInfo, searchProjects, } from './project-selector.js';
|
|
8
|
+
export * from './error-handler.js';
|
|
6
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArchiCore CLI - Project Selector
|
|
3
|
+
*
|
|
4
|
+
* Auto-suggest projects when starting CLI
|
|
5
|
+
*/
|
|
6
|
+
export interface ProjectInfo {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
indexed?: boolean;
|
|
11
|
+
lastAccess?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fetch user's projects from server
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchUserProjects(): Promise<ProjectInfo[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Interactive project selector
|
|
19
|
+
*/
|
|
20
|
+
export declare function selectProject(projects: ProjectInfo[]): Promise<ProjectInfo | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Quick project switcher (for use during session)
|
|
23
|
+
*/
|
|
24
|
+
export declare function quickProjectSwitch(currentProjectId: string | null): Promise<ProjectInfo | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Display project info in a nice format
|
|
27
|
+
*/
|
|
28
|
+
export declare function displayProjectInfo(project: ProjectInfo): void;
|
|
29
|
+
/**
|
|
30
|
+
* Search projects by name
|
|
31
|
+
*/
|
|
32
|
+
export declare function searchProjects(projects: ProjectInfo[], query: string): ProjectInfo[];
|
|
33
|
+
//# sourceMappingURL=project-selector.d.ts.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArchiCore CLI - Project Selector
|
|
3
|
+
*
|
|
4
|
+
* Auto-suggest projects when starting CLI
|
|
5
|
+
*/
|
|
6
|
+
import * as readline from 'readline';
|
|
7
|
+
import { colors, icons } from '../ui/colors.js';
|
|
8
|
+
import { loadConfig } from './config.js';
|
|
9
|
+
/**
|
|
10
|
+
* Fetch user's projects from server
|
|
11
|
+
*/
|
|
12
|
+
export async function fetchUserProjects() {
|
|
13
|
+
try {
|
|
14
|
+
const config = await loadConfig();
|
|
15
|
+
if (!config.accessToken)
|
|
16
|
+
return [];
|
|
17
|
+
const response = await fetch(`${config.serverUrl}/api/projects`, {
|
|
18
|
+
headers: {
|
|
19
|
+
'Authorization': `Bearer ${config.accessToken}`,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok)
|
|
23
|
+
return [];
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
return (data.projects || data || []).map((p) => ({
|
|
26
|
+
id: p.id,
|
|
27
|
+
name: p.name,
|
|
28
|
+
path: p.path,
|
|
29
|
+
indexed: p.indexed,
|
|
30
|
+
lastAccess: p.lastAccess || p.updatedAt,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Interactive project selector
|
|
39
|
+
*/
|
|
40
|
+
export async function selectProject(projects) {
|
|
41
|
+
if (projects.length === 0) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (projects.length === 1) {
|
|
45
|
+
console.log(colors.muted(` Using project: ${colors.primary(projects[0].name)}`));
|
|
46
|
+
return projects[0];
|
|
47
|
+
}
|
|
48
|
+
// Sort by last access
|
|
49
|
+
const sorted = [...projects].sort((a, b) => {
|
|
50
|
+
if (!a.lastAccess)
|
|
51
|
+
return 1;
|
|
52
|
+
if (!b.lastAccess)
|
|
53
|
+
return -1;
|
|
54
|
+
return new Date(b.lastAccess).getTime() - new Date(a.lastAccess).getTime();
|
|
55
|
+
});
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(colors.primary.bold(' Select a project:'));
|
|
58
|
+
console.log();
|
|
59
|
+
sorted.forEach((project, index) => {
|
|
60
|
+
const num = colors.secondary(` ${index + 1}.`);
|
|
61
|
+
const name = colors.highlight(project.name);
|
|
62
|
+
const indexed = project.indexed
|
|
63
|
+
? colors.success(` ${icons.success}`)
|
|
64
|
+
: colors.muted(' (not indexed)');
|
|
65
|
+
const path = project.path ? colors.dim(` ${project.path}`) : '';
|
|
66
|
+
console.log(`${num} ${name}${indexed}${path}`);
|
|
67
|
+
});
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(colors.muted(' Press number to select, Enter for first, or 0 to skip'));
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
const rl = readline.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout,
|
|
74
|
+
terminal: true,
|
|
75
|
+
});
|
|
76
|
+
// Set up timeout for auto-select (5 seconds)
|
|
77
|
+
const timeout = setTimeout(() => {
|
|
78
|
+
console.log(colors.muted(` Auto-selecting: ${sorted[0].name}`));
|
|
79
|
+
rl.close();
|
|
80
|
+
resolve(sorted[0]);
|
|
81
|
+
}, 5000);
|
|
82
|
+
rl.question(colors.primary(` ${icons.arrow} `), (answer) => {
|
|
83
|
+
clearTimeout(timeout);
|
|
84
|
+
rl.close();
|
|
85
|
+
const trimmed = answer.trim();
|
|
86
|
+
if (!trimmed) {
|
|
87
|
+
// Default to first project
|
|
88
|
+
resolve(sorted[0]);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (trimmed === '0') {
|
|
92
|
+
resolve(null);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const index = parseInt(trimmed) - 1;
|
|
96
|
+
if (index >= 0 && index < sorted.length) {
|
|
97
|
+
resolve(sorted[index]);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log(colors.warning(' Invalid selection, using first project'));
|
|
101
|
+
resolve(sorted[0]);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Quick project switcher (for use during session)
|
|
108
|
+
*/
|
|
109
|
+
export async function quickProjectSwitch(currentProjectId) {
|
|
110
|
+
const projects = await fetchUserProjects();
|
|
111
|
+
if (projects.length === 0) {
|
|
112
|
+
console.log(colors.warning(' No projects found'));
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
// Filter out current project
|
|
116
|
+
const available = projects.filter(p => p.id !== currentProjectId);
|
|
117
|
+
if (available.length === 0) {
|
|
118
|
+
console.log(colors.muted(' No other projects available'));
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return selectProject(available);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Display project info in a nice format
|
|
125
|
+
*/
|
|
126
|
+
export function displayProjectInfo(project) {
|
|
127
|
+
console.log();
|
|
128
|
+
console.log(colors.primary.bold(` ${icons.lightning} ${project.name}`));
|
|
129
|
+
if (project.path) {
|
|
130
|
+
console.log(colors.muted(` Path: ${project.path}`));
|
|
131
|
+
}
|
|
132
|
+
if (project.indexed) {
|
|
133
|
+
console.log(colors.success(` Status: Indexed ${icons.success}`));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log(colors.warning(` Status: Not indexed (run /index)`));
|
|
137
|
+
}
|
|
138
|
+
console.log();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Search projects by name
|
|
142
|
+
*/
|
|
143
|
+
export function searchProjects(projects, query) {
|
|
144
|
+
const lower = query.toLowerCase();
|
|
145
|
+
return projects.filter(p => p.name.toLowerCase().includes(lower) ||
|
|
146
|
+
p.path?.toLowerCase().includes(lower));
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=project-selector.js.map
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import chalk from 'chalk';
|
|
|
12
12
|
import ora from 'ora';
|
|
13
13
|
import { writeFile } from 'fs/promises';
|
|
14
14
|
// Import new CLI modules
|
|
15
|
-
import { registerAnalyzerCommands, registerExportCommand, startInteractiveMode, initProject, } from './cli/index.js';
|
|
15
|
+
import { registerAnalyzerCommands, registerExportCommand, registerProjectsCommand, registerAuthCommands, startInteractiveMode, initProject, } from './cli/index.js';
|
|
16
16
|
const program = new Command();
|
|
17
17
|
program
|
|
18
18
|
.name('archicore')
|
|
@@ -29,6 +29,8 @@ program
|
|
|
29
29
|
// Register modular commands
|
|
30
30
|
registerAnalyzerCommands(program);
|
|
31
31
|
registerExportCommand(program);
|
|
32
|
+
registerProjectsCommand(program);
|
|
33
|
+
registerAuthCommands(program);
|
|
32
34
|
// Interactive mode (default when no command)
|
|
33
35
|
program
|
|
34
36
|
.command('chat', { isDefault: false })
|
|
@@ -8,7 +8,7 @@ export declare class ASTParser {
|
|
|
8
8
|
private parseWithRegex;
|
|
9
9
|
private inferTypeFromPattern;
|
|
10
10
|
private extractVueScript;
|
|
11
|
-
parseProject(rootDir: string): Promise<Map<string, ASTNode>>;
|
|
11
|
+
parseProject(rootDir: string, progressCallback?: (current: number, total: number, file: string) => void): Promise<Map<string, ASTNode>>;
|
|
12
12
|
private convertToASTNode;
|
|
13
13
|
private extractName;
|
|
14
14
|
extractLocation(node: Parser.SyntaxNode, filePath: string): Location;
|
|
@@ -278,11 +278,17 @@ export class ASTParser {
|
|
|
278
278
|
isTypeScript
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
|
-
async parseProject(rootDir) {
|
|
281
|
+
async parseProject(rootDir, progressCallback) {
|
|
282
282
|
const files = await FileUtils.getAllFiles(rootDir);
|
|
283
283
|
const asts = new Map();
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
const totalFiles = files.length;
|
|
285
|
+
Logger.progress(`Parsing ${totalFiles} files...`);
|
|
286
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
287
|
+
const file = files[i];
|
|
288
|
+
// Emit progress
|
|
289
|
+
if (progressCallback) {
|
|
290
|
+
progressCallback(i + 1, totalFiles, file);
|
|
291
|
+
}
|
|
286
292
|
const ast = await this.parseFile(file);
|
|
287
293
|
if (ast) {
|
|
288
294
|
asts.set(file, ast);
|
|
@@ -21,7 +21,7 @@ export declare class CodeIndex {
|
|
|
21
21
|
private graph;
|
|
22
22
|
constructor(rootDir?: string);
|
|
23
23
|
indexProject(rootDir?: string): Promise<void>;
|
|
24
|
-
parseProject(): Promise<Map<string, ASTNode>>;
|
|
24
|
+
parseProject(progressCallback?: (current: number, total: number, file: string) => void): Promise<Map<string, ASTNode>>;
|
|
25
25
|
extractSymbols(asts: Map<string, ASTNode>): Map<string, Symbol>;
|
|
26
26
|
buildDependencyGraph(asts: Map<string, ASTNode>, symbols: Map<string, Symbol>): DependencyGraph;
|
|
27
27
|
getGraph(): DependencyGraph | null;
|
package/dist/code-index/index.js
CHANGED
|
@@ -35,8 +35,8 @@ export class CodeIndex {
|
|
|
35
35
|
Logger.success('Project indexed successfully');
|
|
36
36
|
}
|
|
37
37
|
// Методы для использования в ProjectService
|
|
38
|
-
async parseProject() {
|
|
39
|
-
this.asts = await this.astParser.parseProject(this.rootDir);
|
|
38
|
+
async parseProject(progressCallback) {
|
|
39
|
+
this.asts = await this.astParser.parseProject(this.rootDir, progressCallback);
|
|
40
40
|
return this.asts;
|
|
41
41
|
}
|
|
42
42
|
extractSymbols(asts) {
|
|
@@ -103,7 +103,11 @@ export class GitHubService {
|
|
|
103
103
|
state,
|
|
104
104
|
allow_signup: 'true'
|
|
105
105
|
});
|
|
106
|
-
|
|
106
|
+
const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
107
|
+
Logger.info(`GitHub OAuth URL: ${url}`);
|
|
108
|
+
Logger.info(`Requested scopes: ${scopes.join(', ')}`);
|
|
109
|
+
Logger.info(`Redirect URI: ${this.redirectUri}`);
|
|
110
|
+
return url;
|
|
107
111
|
}
|
|
108
112
|
/**
|
|
109
113
|
* Exchange authorization code for access token
|
|
@@ -126,6 +130,9 @@ export class GitHubService {
|
|
|
126
130
|
throw new Error('Failed to exchange code for token');
|
|
127
131
|
}
|
|
128
132
|
const data = await response.json();
|
|
133
|
+
// Debug: log full token response (without sensitive data)
|
|
134
|
+
Logger.info(`GitHub token response keys: ${Object.keys(data).join(', ')}`);
|
|
135
|
+
Logger.info(`GitHub token response scope field: "${data.scope}" (type: ${typeof data.scope})`);
|
|
129
136
|
if (data.error) {
|
|
130
137
|
throw new Error(data.error_description || data.error);
|
|
131
138
|
}
|
|
@@ -11,6 +11,33 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
11
11
|
import OpenAI from 'openai';
|
|
12
12
|
import { Logger } from '../utils/logger.js';
|
|
13
13
|
import { DeepSeekOptimizer } from './deepseek-optimizer.js';
|
|
14
|
+
/**
|
|
15
|
+
* Sanitize file paths to hide server directories
|
|
16
|
+
* Converts absolute paths to relative project paths
|
|
17
|
+
*/
|
|
18
|
+
function sanitizePath(filePath) {
|
|
19
|
+
if (!filePath)
|
|
20
|
+
return filePath;
|
|
21
|
+
// Common server path patterns to strip
|
|
22
|
+
const serverPatterns = [
|
|
23
|
+
/^\/scripts\/archicore\/.archicore\/projects\/[^/]+\//,
|
|
24
|
+
/^\/home\/[^/]+\/[^/]+\/.archicore\/projects\/[^/]+\//,
|
|
25
|
+
/^C:\\[^\\]+\\[^\\]+\\.archicore\\projects\\[^\\]+\\/i,
|
|
26
|
+
/^\/var\/[^/]+\/.archicore\/projects\/[^/]+\//,
|
|
27
|
+
/^.*\.archicore[\/\\]projects[\/\\][^\/\\]+[\/\\]/
|
|
28
|
+
];
|
|
29
|
+
for (const pattern of serverPatterns) {
|
|
30
|
+
if (pattern.test(filePath)) {
|
|
31
|
+
return filePath.replace(pattern, '');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// If path contains .archicore/projects, strip everything before the actual project path
|
|
35
|
+
const archiMatch = filePath.match(/\.archicore[\/\\]projects[\/\\][^\/\\]+[\/\\](.+)/);
|
|
36
|
+
if (archiMatch) {
|
|
37
|
+
return archiMatch[1];
|
|
38
|
+
}
|
|
39
|
+
return filePath;
|
|
40
|
+
}
|
|
14
41
|
export class LLMOrchestrator {
|
|
15
42
|
anthropic;
|
|
16
43
|
openai;
|
|
@@ -291,7 +318,8 @@ ABSOLUTE RULES:
|
|
|
291
318
|
if (context?.semanticMemory && context.semanticMemory.length > 0) {
|
|
292
319
|
prompt += '\n\n###PROJECT FILES (ONLY reference these - nothing else exists)###\n';
|
|
293
320
|
for (const result of context.semanticMemory.slice(0, 10)) {
|
|
294
|
-
|
|
321
|
+
const cleanPath = sanitizePath(result.chunk.metadata.filePath);
|
|
322
|
+
prompt += `\nФайл: ${cleanPath}\n`;
|
|
295
323
|
prompt += `${result.context}\n`;
|
|
296
324
|
}
|
|
297
325
|
prompt += '\n###END PROJECT FILES###';
|
|
@@ -15,7 +15,10 @@ export declare class EmbeddingService {
|
|
|
15
15
|
isAvailable(): boolean;
|
|
16
16
|
generateEmbedding(text: string): Promise<number[]>;
|
|
17
17
|
private generateJinaEmbedding;
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Generate embeddings for multiple texts - uses true batch API when available
|
|
20
|
+
*/
|
|
21
|
+
generateBatchEmbeddings(texts: string[], progressCallback?: (current: number, total: number) => void): Promise<number[][]>;
|
|
19
22
|
private generateOpenAIEmbedding;
|
|
20
23
|
prepareCodeForEmbedding(code: string, context?: string): string;
|
|
21
24
|
generateCodeEmbedding(code: string, metadata: {
|
|
@@ -96,23 +96,70 @@ export class EmbeddingService {
|
|
|
96
96
|
const data = await response.json();
|
|
97
97
|
return data.data[0].embedding;
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Generate embeddings for multiple texts - uses true batch API when available
|
|
101
|
+
*/
|
|
102
|
+
async generateBatchEmbeddings(texts, progressCallback) {
|
|
100
103
|
this.ensureInitialized();
|
|
101
104
|
if (!this._isAvailable) {
|
|
102
105
|
Logger.warn('Embedding service not available, skipping');
|
|
103
106
|
return texts.map(() => new Array(this.embeddingDimension).fill(0));
|
|
104
107
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
if (texts.length === 0)
|
|
109
|
+
return [];
|
|
110
|
+
Logger.progress(`Generating embeddings for ${texts.length} texts...`);
|
|
111
|
+
try {
|
|
112
|
+
// Use true batch API for Jina (much faster!)
|
|
113
|
+
if (this.config.provider === 'jina' && this.jinaApiKey) {
|
|
114
|
+
const BATCH_SIZE = 500;
|
|
115
|
+
const allEmbeddings = [];
|
|
116
|
+
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
|
|
117
|
+
const batch = texts.slice(i, i + BATCH_SIZE).map(t => t.substring(0, 8000));
|
|
118
|
+
const response = await fetch('https://api.jina.ai/v1/embeddings', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
'Authorization': `Bearer ${this.jinaApiKey}`
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
model: this.config.model || 'jina-embeddings-v3',
|
|
126
|
+
task: 'text-matching',
|
|
127
|
+
dimensions: 1024,
|
|
128
|
+
input: batch
|
|
129
|
+
})
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
const error = await response.text();
|
|
133
|
+
throw new Error(`Jina API error: ${response.status} ${error}`);
|
|
134
|
+
}
|
|
135
|
+
const data = await response.json();
|
|
136
|
+
allEmbeddings.push(...data.data.map(d => d.embedding));
|
|
137
|
+
if (progressCallback) {
|
|
138
|
+
progressCallback(Math.min(i + BATCH_SIZE, texts.length), texts.length);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
Logger.success(`Generated ${allEmbeddings.length} embeddings (batch mode)`);
|
|
142
|
+
return allEmbeddings;
|
|
143
|
+
}
|
|
144
|
+
// Fallback: parallel individual calls for OpenAI
|
|
145
|
+
const embeddings = [];
|
|
146
|
+
const CONCURRENT_LIMIT = 10; // Limit concurrent requests
|
|
147
|
+
for (let i = 0; i < texts.length; i += CONCURRENT_LIMIT) {
|
|
148
|
+
const batch = texts.slice(i, i + CONCURRENT_LIMIT);
|
|
149
|
+
const batchEmbeddings = await Promise.all(batch.map(text => this.generateEmbedding(text)));
|
|
150
|
+
embeddings.push(...batchEmbeddings);
|
|
151
|
+
if (progressCallback) {
|
|
152
|
+
progressCallback(Math.min(i + CONCURRENT_LIMIT, texts.length), texts.length);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
Logger.success(`Generated ${embeddings.length} embeddings`);
|
|
156
|
+
return embeddings;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
Logger.error('Batch embedding failed:', error);
|
|
160
|
+
// Return zero vectors on error
|
|
161
|
+
return texts.map(() => new Array(this.embeddingDimension).fill(0));
|
|
113
162
|
}
|
|
114
|
-
Logger.success('Generated ' + embeddings.length + ' embeddings');
|
|
115
|
-
return embeddings;
|
|
116
163
|
}
|
|
117
164
|
async generateOpenAIEmbedding(text) {
|
|
118
165
|
if (!this.openai)
|
|
@@ -15,7 +15,7 @@ export declare class SemanticMemory {
|
|
|
15
15
|
private vectorStore;
|
|
16
16
|
constructor(embeddingConfig: EmbeddingConfig, vectorStoreConfig: VectorStoreConfig);
|
|
17
17
|
initialize(): Promise<void>;
|
|
18
|
-
indexSymbols(symbols: Map<string, Symbol>, asts: Map<string, ASTNode
|
|
18
|
+
indexSymbols(symbols: Map<string, Symbol>, asts: Map<string, ASTNode>, progressCallback?: (current: number, total: number) => void): Promise<void>;
|
|
19
19
|
indexModules(asts: Map<string, ASTNode>): Promise<void>;
|
|
20
20
|
searchByQuery(query: string, limit?: number): Promise<SemanticSearchResult[]>;
|
|
21
21
|
searchSimilarCode(code: string, metadata?: {
|