tabby-ai-assistant 1.0.13 → 1.0.15
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/.editorconfig +18 -0
- package/dist/index.js +1 -1
- package/package.json +6 -4
- package/src/components/chat/ai-sidebar.component.scss +220 -9
- package/src/components/chat/ai-sidebar.component.ts +364 -29
- package/src/components/chat/chat-input.component.ts +36 -4
- package/src/components/chat/chat-interface.component.ts +225 -5
- package/src/components/chat/chat-message.component.ts +6 -1
- package/src/components/settings/context-settings.component.ts +91 -91
- package/src/components/terminal/ai-toolbar-button.component.ts +4 -2
- package/src/components/terminal/command-suggestion.component.ts +148 -6
- package/src/index.ts +0 -6
- package/src/providers/tabby/ai-toolbar-button.provider.ts +7 -3
- package/src/services/chat/ai-sidebar.service.ts +414 -410
- package/src/services/chat/chat-session.service.ts +36 -12
- package/src/services/context/compaction.ts +110 -134
- package/src/services/context/manager.ts +27 -7
- package/src/services/context/memory.ts +17 -33
- package/src/services/context/summary.service.ts +136 -0
- package/src/services/core/ai-assistant.service.ts +1060 -37
- package/src/services/core/ai-provider-manager.service.ts +154 -25
- package/src/services/core/checkpoint.service.ts +218 -18
- package/src/services/core/toast.service.ts +106 -106
- package/src/services/providers/anthropic-provider.service.ts +126 -30
- package/src/services/providers/base-provider.service.ts +90 -7
- package/src/services/providers/glm-provider.service.ts +151 -38
- package/src/services/providers/minimax-provider.service.ts +55 -40
- package/src/services/providers/ollama-provider.service.ts +117 -28
- package/src/services/providers/openai-compatible.service.ts +164 -34
- package/src/services/providers/openai-provider.service.ts +169 -34
- package/src/services/providers/vllm-provider.service.ts +116 -28
- package/src/services/terminal/terminal-context.service.ts +265 -5
- package/src/services/terminal/terminal-manager.service.ts +748 -748
- package/src/services/terminal/terminal-tools.service.ts +612 -441
- package/src/types/ai.types.ts +156 -3
- package/src/utils/cost.utils.ts +249 -0
- package/src/utils/validation.utils.ts +306 -2
- package/dist/index.js.LICENSE.txt +0 -18
- package/src/services/terminal/command-analyzer.service.ts +0 -43
- package/src/services/terminal/context-menu.service.ts +0 -45
- package/src/services/terminal/hotkey.service.ts +0 -53
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
2
|
import { Subject, Observable } from 'rxjs';
|
|
3
|
-
import { TerminalContext, TerminalSession, TerminalError, CommandResult, SystemInfo, ProcessInfo } from '../../types/terminal.types';
|
|
3
|
+
import { TerminalContext, TerminalSession, TerminalError, CommandResult, SystemInfo, ProcessInfo, ProjectInfo } from '../../types/terminal.types';
|
|
4
4
|
import { LoggerService } from '../core/logger.service';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 项目检测配置
|
|
8
|
+
*/
|
|
9
|
+
interface ProjectDetector {
|
|
10
|
+
pattern: RegExp;
|
|
11
|
+
type: ProjectInfo['type'];
|
|
12
|
+
configFiles: string[];
|
|
13
|
+
parseConfig: (content: string) => Partial<ProjectInfo>;
|
|
14
|
+
language: string;
|
|
15
|
+
framework?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
6
18
|
@Injectable({ providedIn: 'root' })
|
|
7
19
|
export class TerminalContextService {
|
|
8
20
|
private currentContext: TerminalContext | null = null;
|
|
@@ -10,6 +22,127 @@ export class TerminalContextService {
|
|
|
10
22
|
private errorDetected$ = new Subject<TerminalError>();
|
|
11
23
|
private commandExecuted$ = new Subject<CommandResult>();
|
|
12
24
|
|
|
25
|
+
// 项目检测器配置
|
|
26
|
+
private readonly projectDetectors: ProjectDetector[] = [
|
|
27
|
+
{
|
|
28
|
+
pattern: /package\.json$/,
|
|
29
|
+
type: 'npm',
|
|
30
|
+
configFiles: ['package.json'],
|
|
31
|
+
parseConfig: (content: string) => {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(content);
|
|
34
|
+
return {
|
|
35
|
+
name: pkg.name,
|
|
36
|
+
version: pkg.version,
|
|
37
|
+
dependencies: Object.keys(pkg.dependencies || {}),
|
|
38
|
+
scripts: pkg.scripts,
|
|
39
|
+
description: pkg.description,
|
|
40
|
+
framework: pkg.dependencies ? this.detectFramework(Object.keys(pkg.dependencies)) : undefined
|
|
41
|
+
};
|
|
42
|
+
} catch {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
language: 'JavaScript/TypeScript'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
pattern: /pom\.xml$/,
|
|
50
|
+
type: 'maven',
|
|
51
|
+
configFiles: ['pom.xml'],
|
|
52
|
+
parseConfig: (content: string) => {
|
|
53
|
+
const nameMatch = content.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
54
|
+
const versionMatch = content.match(/<version>([^<]+)<\/version>/);
|
|
55
|
+
return {
|
|
56
|
+
name: nameMatch?.[1],
|
|
57
|
+
version: versionMatch?.[1],
|
|
58
|
+
language: 'Java'
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
language: 'Java'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
pattern: /build\.gradle$/,
|
|
65
|
+
type: 'gradle',
|
|
66
|
+
configFiles: ['build.gradle', 'build.gradle.kts'],
|
|
67
|
+
parseConfig: (content: string) => {
|
|
68
|
+
const nameMatch = content.match(/rootProject\.name\s*=\s*['"]([^'"]+)['"]/);
|
|
69
|
+
const versionMatch = content.match(/version\s*=\s*['"]([^'"]+)['"]/);
|
|
70
|
+
return {
|
|
71
|
+
name: nameMatch?.[1],
|
|
72
|
+
version: versionMatch?.[1],
|
|
73
|
+
language: 'Java/Kotlin'
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
language: 'Java/Kotlin'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
pattern: /requirements\.txt$/,
|
|
80
|
+
type: 'pip',
|
|
81
|
+
configFiles: ['requirements.txt'],
|
|
82
|
+
parseConfig: (content: string) => {
|
|
83
|
+
const deps = content.split('\n')
|
|
84
|
+
.map(line => line.split(/[>=<!]/)[0].trim())
|
|
85
|
+
.filter(d => d.length > 0);
|
|
86
|
+
return {
|
|
87
|
+
dependencies: deps,
|
|
88
|
+
language: 'Python',
|
|
89
|
+
framework: this.detectPythonFramework(deps)
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
language: 'Python'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
pattern: /Cargo\.toml$/,
|
|
96
|
+
type: 'cargo',
|
|
97
|
+
configFiles: ['Cargo.toml'],
|
|
98
|
+
parseConfig: (content: string) => {
|
|
99
|
+
const nameMatch = content.match(/name\s*=\s*["']([^"']+)["']/);
|
|
100
|
+
const versionMatch = content.match(/version\s*=\s*["']([^"']+)["']/);
|
|
101
|
+
return {
|
|
102
|
+
name: nameMatch?.[1],
|
|
103
|
+
version: versionMatch?.[1],
|
|
104
|
+
language: 'Rust'
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
language: 'Rust'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
pattern: /go\.mod$/,
|
|
111
|
+
type: 'go',
|
|
112
|
+
configFiles: ['go.mod'],
|
|
113
|
+
parseConfig: (content: string) => {
|
|
114
|
+
const moduleMatch = content.match(/module\s+([^\s]+)/);
|
|
115
|
+
const versionMatch = content.match(/go\s+([\d.]+)/);
|
|
116
|
+
return {
|
|
117
|
+
name: moduleMatch?.[1],
|
|
118
|
+
version: versionMatch?.[1],
|
|
119
|
+
language: 'Go'
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
language: 'Go'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
pattern: /yarn\.lock$/,
|
|
126
|
+
type: 'yarn',
|
|
127
|
+
configFiles: ['package.json', 'yarn.lock'],
|
|
128
|
+
parseConfig: (content: string) => {
|
|
129
|
+
try {
|
|
130
|
+
const pkg = JSON.parse(content);
|
|
131
|
+
return {
|
|
132
|
+
name: pkg.name,
|
|
133
|
+
version: pkg.version,
|
|
134
|
+
dependencies: Object.keys(pkg.dependencies || {}),
|
|
135
|
+
scripts: pkg.scripts,
|
|
136
|
+
description: pkg.description
|
|
137
|
+
};
|
|
138
|
+
} catch {
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
language: 'JavaScript/TypeScript'
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
|
|
13
146
|
constructor(private logger: LoggerService) {
|
|
14
147
|
this.initializeContext();
|
|
15
148
|
}
|
|
@@ -25,7 +158,7 @@ export class TerminalContextService {
|
|
|
25
158
|
isRunning: false,
|
|
26
159
|
recentCommands: [],
|
|
27
160
|
systemInfo,
|
|
28
|
-
projectInfo
|
|
161
|
+
projectInfo: projectInfo || undefined
|
|
29
162
|
};
|
|
30
163
|
|
|
31
164
|
this.logger.info('Terminal context initialized', { context: this.currentContext });
|
|
@@ -244,13 +377,140 @@ export class TerminalContextService {
|
|
|
244
377
|
|
|
245
378
|
/**
|
|
246
379
|
* 检测项目信息
|
|
380
|
+
* 根据当前工作目录中的配置文件检测项目类型和元数据
|
|
247
381
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
382
|
+
async detectProjectInfo(): Promise<ProjectInfo | null> {
|
|
383
|
+
const cwd = this.currentContext?.session.cwd || process.cwd();
|
|
384
|
+
|
|
385
|
+
// 检测 .git 目录(Git 项目)
|
|
386
|
+
const hasGit = await this.checkFileExists('.git');
|
|
387
|
+
if (hasGit) {
|
|
388
|
+
return {
|
|
389
|
+
type: 'git',
|
|
390
|
+
root: cwd,
|
|
391
|
+
name: this.extractProjectName(cwd),
|
|
392
|
+
language: 'N/A'
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// 遍历项目检测器
|
|
397
|
+
for (const detector of this.projectDetectors) {
|
|
398
|
+
for (const configFile of detector.configFiles) {
|
|
399
|
+
const content = await this.readFileContent(configFile);
|
|
400
|
+
if (content) {
|
|
401
|
+
const config = detector.parseConfig(content);
|
|
402
|
+
return {
|
|
403
|
+
type: detector.type,
|
|
404
|
+
root: cwd,
|
|
405
|
+
name: config.name || this.extractProjectName(cwd),
|
|
406
|
+
version: config.version,
|
|
407
|
+
dependencies: config.dependencies,
|
|
408
|
+
scripts: config.scripts,
|
|
409
|
+
description: config.description,
|
|
410
|
+
language: detector.language,
|
|
411
|
+
framework: config.framework || detector.framework
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// 未检测到项目
|
|
251
418
|
return null;
|
|
252
419
|
}
|
|
253
420
|
|
|
421
|
+
/**
|
|
422
|
+
* 手动触发项目重新检测
|
|
423
|
+
*/
|
|
424
|
+
async refreshProjectInfo(): Promise<void> {
|
|
425
|
+
if (!this.currentContext) return;
|
|
426
|
+
|
|
427
|
+
const projectInfo = await this.detectProjectInfo();
|
|
428
|
+
this.currentContext.projectInfo = projectInfo || undefined;
|
|
429
|
+
this.contextChange$.next(this.currentContext);
|
|
430
|
+
|
|
431
|
+
this.logger.info('Project info refreshed', { projectInfo });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* 获取所有检测到的项目类型
|
|
436
|
+
*/
|
|
437
|
+
getSupportedProjectTypes(): ProjectInfo['type'][] {
|
|
438
|
+
return this.projectDetectors.map(d => d.type);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* 检查文件是否存在(模拟实现)
|
|
443
|
+
*/
|
|
444
|
+
private async checkFileExists(_filename: string): Promise<boolean> {
|
|
445
|
+
// 在浏览器环境中,此方法需要与实际的 Tabby API 集成
|
|
446
|
+
// 这里返回模拟值用于演示
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 读取文件内容(模拟实现)
|
|
452
|
+
*/
|
|
453
|
+
private async readFileContent(_filename: string): Promise<string | null> {
|
|
454
|
+
// 在浏览器环境中,此方法需要与实际的 Tabby API 集成
|
|
455
|
+
// 这里返回模拟值用于演示
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* 从路径提取项目名称
|
|
461
|
+
*/
|
|
462
|
+
private extractProjectName(path: string): string {
|
|
463
|
+
const parts = path.split('/');
|
|
464
|
+
return parts[parts.length - 1] || 'unknown-project';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* 检测 JavaScript/TypeScript 项目框架
|
|
469
|
+
*/
|
|
470
|
+
private detectFramework(dependencies: string[]): string | undefined {
|
|
471
|
+
const frameworkIndicators: { [key: string]: string[] } = {
|
|
472
|
+
'React': ['react', 'react-dom'],
|
|
473
|
+
'Vue': ['vue'],
|
|
474
|
+
'Angular': ['@angular/core'],
|
|
475
|
+
'Next.js': ['next'],
|
|
476
|
+
'Nuxt': ['nuxt'],
|
|
477
|
+
'Svelte': ['svelte'],
|
|
478
|
+
'Express': ['express'],
|
|
479
|
+
'NestJS': ['@nestjs/core'],
|
|
480
|
+
'Electron': ['electron'],
|
|
481
|
+
'Expo': ['expo']
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
for (const [framework, indicators] of Object.entries(frameworkIndicators)) {
|
|
485
|
+
if (indicators.some(ind => dependencies.includes(ind))) {
|
|
486
|
+
return framework;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* 检测 Python 项目框架
|
|
494
|
+
*/
|
|
495
|
+
private detectPythonFramework(dependencies: string[]): string | undefined {
|
|
496
|
+
const frameworkIndicators: { [key: string]: string[] } = {
|
|
497
|
+
'Django': ['django'],
|
|
498
|
+
'Flask': ['flask'],
|
|
499
|
+
'FastAPI': ['fastapi'],
|
|
500
|
+
'Pyramid': ['pyramid'],
|
|
501
|
+
'Tornado': ['tornado'],
|
|
502
|
+
'Web2py': ['web2py'],
|
|
503
|
+
'CherryPy': ['cherrypy']
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
for (const [framework, indicators] of Object.entries(frameworkIndicators)) {
|
|
507
|
+
if (indicators.some(ind => dependencies.includes(ind))) {
|
|
508
|
+
return framework;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
|
|
254
514
|
/**
|
|
255
515
|
* 检测当前Shell
|
|
256
516
|
*/
|