@opensumi/ide-ai-native 3.9.1-next-1749007675.0 → 3.9.1-next-1749008258.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/lib/browser/chat/chat-manager.service.d.ts +0 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +3 -8
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts +1 -3
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +17 -48
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts +0 -2
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +50 -57
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.feature.registry.d.ts +1 -4
- package/lib/browser/chat/chat.feature.registry.d.ts.map +1 -1
- package/lib/browser/chat/chat.feature.registry.js +0 -6
- package/lib/browser/chat/chat.feature.registry.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +12 -29
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +141 -30
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/ChatToolRender.module.less +1 -0
- package/lib/browser/components/components.module.less +9 -8
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +161 -14
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +165 -1
- package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
- package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
- package/lib/browser/components/mention-input/mention-select.js +136 -0
- package/lib/browser/components/mention-input/mention-select.js.map +1 -0
- package/lib/browser/components/mention-input/mention-select.module.less +297 -0
- package/lib/browser/components/mention-input/types.d.ts +16 -1
- package/lib/browser/components/mention-input/types.d.ts.map +1 -1
- package/lib/browser/components/mention-input/types.js +1 -0
- package/lib/browser/components/mention-input/types.js.map +1 -1
- package/lib/browser/components/utils.d.ts +2 -2
- package/lib/browser/context/llm-context.service.d.ts +21 -2
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +162 -20
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
- package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
- package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
- package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
- package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +7 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/base-apply.service.d.ts +2 -2
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
- package/lib/browser/mcp/base-apply.service.js.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
- package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +1 -39
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js +3 -170
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +5 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/browser/rules/rules.contribution.d.ts +29 -0
- package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
- package/lib/browser/rules/rules.contribution.js +94 -0
- package/lib/browser/rules/rules.contribution.js.map +1 -0
- package/lib/browser/rules/rules.module.less +175 -0
- package/lib/browser/rules/rules.service.d.ts +25 -0
- package/lib/browser/rules/rules.service.d.ts.map +1 -0
- package/lib/browser/rules/rules.service.js +180 -0
- package/lib/browser/rules/rules.service.js.map +1 -0
- package/lib/browser/rules/rules.view.d.ts +3 -0
- package/lib/browser/rules/rules.view.d.ts.map +1 -0
- package/lib/browser/rules/rules.view.js +76 -0
- package/lib/browser/rules/rules.view.js.map +1 -0
- package/lib/browser/types.d.ts +1 -8
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
- package/lib/common/MDC_PARSER_README.md +171 -0
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +2 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/llm-context.d.ts +19 -0
- package/lib/common/llm-context.d.ts.map +1 -1
- package/lib/common/llm-context.js.map +1 -1
- package/lib/common/mdc-parser.d.ts +60 -0
- package/lib/common/mdc-parser.d.ts.map +1 -0
- package/lib/common/mdc-parser.js +246 -0
- package/lib/common/mdc-parser.js.map +1 -0
- package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +35 -29
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/lib/common/prompts/system-prompt.d.ts +2 -0
- package/lib/common/prompts/system-prompt.d.ts.map +1 -0
- package/lib/common/prompts/system-prompt.js +5 -0
- package/lib/common/prompts/system-prompt.js.map +1 -0
- package/lib/common/types.d.ts +7 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js.map +1 -1
- package/package.json +25 -24
- package/src/browser/chat/chat-manager.service.ts +6 -15
- package/src/browser/chat/chat-model.ts +19 -56
- package/src/browser/chat/chat-proxy.service.ts +68 -81
- package/src/browser/chat/chat.feature.registry.ts +1 -17
- package/src/browser/chat/chat.view.tsx +22 -28
- package/src/browser/components/ChatMentionInput.tsx +169 -35
- package/src/browser/components/ChatToolRender.module.less +1 -0
- package/src/browser/components/components.module.less +9 -8
- package/src/browser/components/mention-input/mention-input.module.less +165 -1
- package/src/browser/components/mention-input/mention-input.tsx +257 -32
- package/src/browser/components/mention-input/mention-select.module.less +297 -0
- package/src/browser/components/mention-input/mention-select.tsx +256 -0
- package/src/browser/components/mention-input/types.ts +16 -0
- package/src/browser/context/llm-context.service.ts +182 -21
- package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
- package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
- package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
- package/src/browser/index.ts +8 -0
- package/src/browser/mcp/base-apply.service.ts +0 -1
- package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
- package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
- package/src/browser/mcp/tools/handlers/ListDir.ts +1 -1
- package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
- package/src/browser/model/msg-history-manager.ts +3 -230
- package/src/browser/preferences/schema.ts +5 -0
- package/src/browser/rules/rules.contribution.ts +105 -0
- package/src/browser/rules/rules.module.less +175 -0
- package/src/browser/rules/rules.service.ts +189 -0
- package/src/browser/rules/rules.view.tsx +127 -0
- package/src/browser/types.ts +0 -12
- package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
- package/src/common/MDC_PARSER_README.md +171 -0
- package/src/common/index.ts +3 -0
- package/src/common/llm-context.ts +23 -0
- package/src/common/mdc-parser.ts +295 -0
- package/src/common/prompts/context-prompt-provider.ts +55 -40
- package/src/common/prompts/system-prompt.ts +2 -0
- package/src/common/types.ts +8 -0
- package/src/node/base-language-model.ts +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
|
+
import { VALIDATE_TYPE } from '@opensumi/ide-components';
|
|
3
|
+
import { AppConfig, PreferenceService } from '@opensumi/ide-core-browser';
|
|
4
|
+
import {
|
|
5
|
+
AINativeSettingSectionsId,
|
|
6
|
+
Disposable,
|
|
7
|
+
Emitter,
|
|
8
|
+
URI,
|
|
9
|
+
formatLocalize,
|
|
10
|
+
localize,
|
|
11
|
+
} from '@opensumi/ide-core-common';
|
|
12
|
+
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
13
|
+
import { IFileServiceClient } from '@opensumi/ide-file-service';
|
|
14
|
+
import { QuickInputService } from '@opensumi/ide-quick-open/lib/browser/quick-input-service';
|
|
15
|
+
import { WorkspaceService } from '@opensumi/ide-workspace/lib/browser/workspace-service';
|
|
16
|
+
import { IWorkspaceService } from '@opensumi/ide-workspace/lib/common';
|
|
17
|
+
|
|
18
|
+
import { IMDCContent, IMDCParseResult, parseMDC, serializeMDC } from '../../common';
|
|
19
|
+
import { ProjectRule } from '../../common/types';
|
|
20
|
+
|
|
21
|
+
@Injectable()
|
|
22
|
+
export class RulesService extends Disposable {
|
|
23
|
+
@Autowired(PreferenceService)
|
|
24
|
+
private readonly preferenceService: PreferenceService;
|
|
25
|
+
|
|
26
|
+
@Autowired(AppConfig)
|
|
27
|
+
private readonly appConfig: AppConfig;
|
|
28
|
+
|
|
29
|
+
@Autowired(IWorkspaceService)
|
|
30
|
+
private readonly workspaceService: WorkspaceService;
|
|
31
|
+
|
|
32
|
+
@Autowired(IFileServiceClient)
|
|
33
|
+
private readonly fileServiceClient: IFileServiceClient;
|
|
34
|
+
|
|
35
|
+
@Autowired(QuickInputService)
|
|
36
|
+
private readonly quickInputService: QuickInputService;
|
|
37
|
+
|
|
38
|
+
@Autowired(WorkbenchEditorService)
|
|
39
|
+
private readonly workbenchEditorService: WorkbenchEditorService;
|
|
40
|
+
|
|
41
|
+
private readonly rulesChangeEventEmitter = new Emitter<void>();
|
|
42
|
+
|
|
43
|
+
private _globalRules: string;
|
|
44
|
+
private _projectRules: ProjectRule[];
|
|
45
|
+
|
|
46
|
+
private _projectRulesPath: string;
|
|
47
|
+
|
|
48
|
+
get onRulesChange() {
|
|
49
|
+
return this.rulesChangeEventEmitter.event;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get globalRules() {
|
|
53
|
+
if (!this._globalRules) {
|
|
54
|
+
this._globalRules = this.preferenceService.get<string>(AINativeSettingSectionsId.GlobalRules) || '';
|
|
55
|
+
}
|
|
56
|
+
return this._globalRules;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get projectRules() {
|
|
60
|
+
return this._projectRules || [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public async initProjectRules() {
|
|
64
|
+
this.dispose();
|
|
65
|
+
await this.workspaceService.whenReady;
|
|
66
|
+
const workspace = this.workspaceService.workspace;
|
|
67
|
+
// 如果存在 .sumi, 默认从 .sumi 中获取
|
|
68
|
+
const sumiConfigPath = new URI(workspace?.uri)
|
|
69
|
+
.resolve(this.appConfig.preferenceDirName || '.sumi')
|
|
70
|
+
.resolve('rules');
|
|
71
|
+
const cursorConfigPath = new URI(workspace?.uri).resolve('.cursor').resolve('rules');
|
|
72
|
+
if (await this.fileServiceClient.access(sumiConfigPath.codeUri.fsPath)) {
|
|
73
|
+
this._projectRulesPath = sumiConfigPath.codeUri.fsPath;
|
|
74
|
+
} else if (await this.fileServiceClient.access(cursorConfigPath.codeUri.fsPath)) {
|
|
75
|
+
this._projectRulesPath = cursorConfigPath.codeUri.fsPath;
|
|
76
|
+
} else {
|
|
77
|
+
this._projectRulesPath = sumiConfigPath.codeUri.fsPath;
|
|
78
|
+
}
|
|
79
|
+
const watcher = await this.fileServiceClient.watchFileChanges(new URI(this._projectRulesPath));
|
|
80
|
+
this.addDispose(
|
|
81
|
+
watcher.onFilesChanged((changes) => {
|
|
82
|
+
if (
|
|
83
|
+
changes.length > 0 &&
|
|
84
|
+
changes.some((change) => change.uri.endsWith('.mdc') && change.uri.includes('rules'))
|
|
85
|
+
) {
|
|
86
|
+
this.initProjectRules();
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
const dir = await this.fileServiceClient.getFileStat(this._projectRulesPath);
|
|
91
|
+
if (dir && dir.isDirectory) {
|
|
92
|
+
const files = dir.children?.filter((file) => !file.isDirectory && file.uri.endsWith('.mdc'));
|
|
93
|
+
if (files) {
|
|
94
|
+
const rules = await Promise.all(
|
|
95
|
+
files.map(async (file) => {
|
|
96
|
+
const item = await this.fileServiceClient.readFile(file.uri);
|
|
97
|
+
const data = this.parseMDCContent(item.content.toString());
|
|
98
|
+
return {
|
|
99
|
+
path: file.uri,
|
|
100
|
+
...data.frontmatter,
|
|
101
|
+
content: data.content,
|
|
102
|
+
};
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
if (rules.length > 0) {
|
|
106
|
+
this._projectRules = rules;
|
|
107
|
+
this.rulesChangeEventEmitter.fire();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this._projectRules = [];
|
|
113
|
+
this.rulesChangeEventEmitter.fire();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async openRule(rule: ProjectRule) {
|
|
117
|
+
this.workbenchEditorService.open(new URI(rule.path));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async createNewRule() {
|
|
121
|
+
let value = await this.quickInputService.open({
|
|
122
|
+
title: localize('ai.native.rules.projectRules.newRule'),
|
|
123
|
+
value: '',
|
|
124
|
+
placeHolder: localize('ai.native.rules.projectRules.newRule.placeholder'),
|
|
125
|
+
validateInput: async (value) => {
|
|
126
|
+
const invalidCharsRegex = /[<>:"/\\|?*\x00-\x1F]/;
|
|
127
|
+
value = value.trim();
|
|
128
|
+
if (value === '') {
|
|
129
|
+
return {
|
|
130
|
+
message: localize('ai.native.rules.projectRules.newRule.error.notEmpty'),
|
|
131
|
+
type: VALIDATE_TYPE.ERROR,
|
|
132
|
+
};
|
|
133
|
+
} else if (invalidCharsRegex.test(value)) {
|
|
134
|
+
return {
|
|
135
|
+
message: localize('ai.native.rules.error.invalidFileName'),
|
|
136
|
+
type: VALIDATE_TYPE.ERROR,
|
|
137
|
+
};
|
|
138
|
+
} else {
|
|
139
|
+
const newRulePath = new URI(this._projectRulesPath).resolve(value + '.mdc');
|
|
140
|
+
const fileExists = this.projectRules.find(
|
|
141
|
+
(rule) => new URI(rule.path).codeUri.fsPath === newRulePath.codeUri.fsPath,
|
|
142
|
+
);
|
|
143
|
+
if (fileExists) {
|
|
144
|
+
return {
|
|
145
|
+
message: formatLocalize('ai.native.rules.error.fileExists', value + '.mdc'),
|
|
146
|
+
type: VALIDATE_TYPE.ERROR,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
value = value?.trim();
|
|
153
|
+
if (value) {
|
|
154
|
+
const newRulePath = new URI(this._projectRulesPath).resolve(value + '.mdc');
|
|
155
|
+
await this.fileServiceClient.createFile(newRulePath.toString(), {
|
|
156
|
+
content: this.serializeMDCContent({
|
|
157
|
+
frontmatter: {
|
|
158
|
+
description: '',
|
|
159
|
+
globs: '',
|
|
160
|
+
alwaysApply: false,
|
|
161
|
+
},
|
|
162
|
+
content: '',
|
|
163
|
+
}),
|
|
164
|
+
});
|
|
165
|
+
this.workbenchEditorService.open(new URI(newRulePath.toString()));
|
|
166
|
+
this.initProjectRules();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
updateGlobalRules(rules: string) {
|
|
171
|
+
this._globalRules = rules;
|
|
172
|
+
this.preferenceService.set(AINativeSettingSectionsId.GlobalRules, rules);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
parseMDCContent(content: string): IMDCParseResult {
|
|
176
|
+
try {
|
|
177
|
+
return parseMDC(content);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
return {
|
|
180
|
+
frontmatter: {},
|
|
181
|
+
content,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
serializeMDCContent(mdcContent: IMDCContent): string {
|
|
187
|
+
return serializeMDC(mdcContent);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Icon } from '@opensumi/ide-components';
|
|
4
|
+
import { useInjectable } from '@opensumi/ide-core-browser';
|
|
5
|
+
import { RulesServiceToken, localize } from '@opensumi/ide-core-common';
|
|
6
|
+
|
|
7
|
+
import { ProjectRule } from '../../common/types';
|
|
8
|
+
|
|
9
|
+
import styles from './rules.module.less';
|
|
10
|
+
import { RulesService } from './rules.service';
|
|
11
|
+
|
|
12
|
+
export const RulesView: React.FC = () => {
|
|
13
|
+
const rulesService = useInjectable<RulesService>(RulesServiceToken);
|
|
14
|
+
const [globalRules, setGlobalRules] = useState<string>(rulesService.globalRules as string);
|
|
15
|
+
const [projectRules, setProjectRules] = useState<ProjectRule[]>([]);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setProjectRules(rulesService.projectRules);
|
|
19
|
+
}, [rulesService]);
|
|
20
|
+
|
|
21
|
+
const handleGlobalRulesChange = useCallback(
|
|
22
|
+
(e) => {
|
|
23
|
+
setGlobalRules(e.target.value);
|
|
24
|
+
rulesService.updateGlobalRules(e.target.value);
|
|
25
|
+
},
|
|
26
|
+
[rulesService, globalRules],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const handleCreateNewRule = useCallback(() => {
|
|
30
|
+
rulesService.createNewRule();
|
|
31
|
+
}, [rulesService]);
|
|
32
|
+
|
|
33
|
+
const handleOpenRule = useCallback(
|
|
34
|
+
(rule: ProjectRule) => {
|
|
35
|
+
rulesService.openRule(rule);
|
|
36
|
+
},
|
|
37
|
+
[rulesService],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const getFileNameFromPath = (path: string) => decodeURIComponent(path.split('/').pop() || path);
|
|
41
|
+
|
|
42
|
+
const hasWarning = (rule: ProjectRule) => !rule.description && !rule.alwaysApply && !rule.globs;
|
|
43
|
+
|
|
44
|
+
const getAppliesToText = (rule: ProjectRule) => {
|
|
45
|
+
if (rule.globs) {
|
|
46
|
+
if (Array.isArray(rule.globs)) {
|
|
47
|
+
return `Applies to: ${rule.globs.join(', ')}`;
|
|
48
|
+
} else {
|
|
49
|
+
return `Applies to: ${rule.globs}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const disposable = rulesService.onRulesChange(() => {
|
|
57
|
+
setProjectRules(rulesService.projectRules);
|
|
58
|
+
});
|
|
59
|
+
return () => {
|
|
60
|
+
disposable.dispose();
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={styles.container}>
|
|
66
|
+
<div className={styles.header}>
|
|
67
|
+
<div>
|
|
68
|
+
<h2 className={styles.title}>{localize('ai.native.rules.title')}</h2>
|
|
69
|
+
<p className={styles.description}>{localize('ai.native.rules.description')}</p>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div className={styles.user_rules}>
|
|
73
|
+
<div className={styles.user_rules_header}>
|
|
74
|
+
<h3 className={styles.title}>{localize('ai.native.rules.userRules.title')}</h3>
|
|
75
|
+
<p className={styles.description}>{localize('ai.native.rules.userRules.description')}</p>
|
|
76
|
+
</div>
|
|
77
|
+
<div className={styles.user_rules_content}>
|
|
78
|
+
<textarea
|
|
79
|
+
rows={6}
|
|
80
|
+
placeholder={localize('ai.native.rules.userRules.placeholder')}
|
|
81
|
+
value={globalRules}
|
|
82
|
+
onChange={handleGlobalRulesChange}
|
|
83
|
+
style={{ resize: 'none' }}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div className={styles.project_rules}>
|
|
88
|
+
<div className={styles.project_rules_header}>
|
|
89
|
+
<div className={styles.project_rules_header_left}>
|
|
90
|
+
<h3 className={styles.title}>
|
|
91
|
+
{localize('ai.native.rules.projectRules.title')}
|
|
92
|
+
<button className={styles.actionButton} onClick={handleCreateNewRule}>
|
|
93
|
+
<Icon icon='plus' className={styles.actionButtonIcon} />
|
|
94
|
+
{localize('ai.native.rules.projectRules.newRule')}
|
|
95
|
+
</button>
|
|
96
|
+
</h3>
|
|
97
|
+
<p className={styles.description}>{localize('ai.native.rules.projectRules.description')}</p>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div className={styles.project_rules_content}>
|
|
101
|
+
{projectRules.length > 0 ? (
|
|
102
|
+
<div className={styles.project_rules_list}>
|
|
103
|
+
{projectRules.map((rule, index) => (
|
|
104
|
+
<div key={index} className={styles.rule_item} onClick={() => handleOpenRule(rule)}>
|
|
105
|
+
<div className={styles.rule_item_left}>
|
|
106
|
+
<div className={styles.rule_filename}>{getFileNameFromPath(rule.path)}</div>
|
|
107
|
+
{rule.description && <div className={styles.rule_description}>{rule.description}</div>}
|
|
108
|
+
{hasWarning(rule) && (
|
|
109
|
+
<div className={styles.rule_warning}>
|
|
110
|
+
This rule may never be used since it has no description or auto attachments
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
<div className={styles.rule_item_right}>
|
|
115
|
+
{getAppliesToText(rule) && <div className={styles.rule_applies_to}>{getAppliesToText(rule)}</div>}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
) : (
|
|
121
|
+
<div className={styles.empty_state}>{localize('ai.native.rules.projectRules.empty')}</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
};
|
package/src/browser/types.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { ZodSchema } from 'zod';
|
|
|
5
5
|
import { AIActionItem } from '@opensumi/ide-core-browser/lib/components/ai-native/index';
|
|
6
6
|
import {
|
|
7
7
|
CancellationToken,
|
|
8
|
-
ChatMessageRole,
|
|
9
8
|
ChatResponse,
|
|
10
9
|
Deferred,
|
|
11
10
|
IAICompletionOption,
|
|
@@ -135,8 +134,6 @@ export interface IChatFeatureRegistry {
|
|
|
135
134
|
registerImageUploadProvider(provider: IImageUploadProvider): void;
|
|
136
135
|
registerWelcome(content: IChatWelcomeMessageContent | React.ReactNode, sampleQuestions?: ISampleQuestions[]): void;
|
|
137
136
|
registerSlashCommand(command: IChatSlashCommandItem, handler: IChatSlashCommandHandler): void;
|
|
138
|
-
|
|
139
|
-
registerMessageSummaryProvider(provider: IMessageSummaryProvider): void;
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
export type ChatWelcomeRender = (props: {
|
|
@@ -301,15 +298,6 @@ export interface IImageUploadProvider {
|
|
|
301
298
|
imageUpload(file: File): Promise<DataContent | URL>;
|
|
302
299
|
}
|
|
303
300
|
|
|
304
|
-
export interface IMessageSummaryProvider {
|
|
305
|
-
getMessageSummary(
|
|
306
|
-
messages: Array<{
|
|
307
|
-
role: ChatMessageRole;
|
|
308
|
-
content: string;
|
|
309
|
-
}>,
|
|
310
|
-
): Promise<string | undefined>;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
301
|
export const AINativeCoreContribution = Symbol('AINativeCoreContribution');
|
|
314
302
|
|
|
315
303
|
export interface AINativeCoreContribution {
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
Position,
|
|
14
14
|
} from '@opensumi/ide-monaco';
|
|
15
15
|
import { ReactInlineContentWidget } from '@opensumi/ide-monaco/lib/browser/ai-native/BaseInlineContentWidget';
|
|
16
|
-
import { URI } from '@opensumi/ide-monaco/lib/browser/monaco-api';
|
|
17
16
|
import { ContentWidgetPositionPreference } from '@opensumi/ide-monaco/lib/browser/monaco-exports/editor';
|
|
18
17
|
import { EditorOption } from '@opensumi/monaco-editor-core/esm/vs/editor/common/config/editorOptions';
|
|
19
18
|
import { IScrollEvent } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorCommon';
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# MDC 文件解析器
|
|
2
|
+
|
|
3
|
+
MDC (Markdown with YAML Frontmatter) 解析器用于解析和序列化带有 YAML frontmatter 的 Markdown 文件。
|
|
4
|
+
|
|
5
|
+
## 文件格式
|
|
6
|
+
|
|
7
|
+
MDC 文件格式如下:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
---
|
|
11
|
+
description:
|
|
12
|
+
globs:
|
|
13
|
+
alwaysApply: false
|
|
14
|
+
---
|
|
15
|
+
icejs is a react-like framework
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
其中:
|
|
19
|
+
|
|
20
|
+
- `---` 分隔符之间的部分是 YAML frontmatter
|
|
21
|
+
- 分隔符之后的部分是 Markdown 内容
|
|
22
|
+
|
|
23
|
+
## API 接口
|
|
24
|
+
|
|
25
|
+
### 类型定义
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
interface IMDCFrontmatter {
|
|
29
|
+
description?: string;
|
|
30
|
+
globs?: string | string[];
|
|
31
|
+
alwaysApply?: boolean;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface IMDCParseResult {
|
|
36
|
+
frontmatter: IMDCFrontmatter;
|
|
37
|
+
content: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface IMDCContent {
|
|
41
|
+
frontmatter: IMDCFrontmatter;
|
|
42
|
+
content: string;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 主要函数
|
|
47
|
+
|
|
48
|
+
#### `parseMDC(rawContent: string): IMDCParseResult`
|
|
49
|
+
|
|
50
|
+
解析 MDC 文件内容,返回解析后的 frontmatter 和内容。
|
|
51
|
+
|
|
52
|
+
**示例:**
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const content = `---
|
|
56
|
+
description: "React规则"
|
|
57
|
+
globs: ["*.tsx", "*.jsx"]
|
|
58
|
+
alwaysApply: true
|
|
59
|
+
---
|
|
60
|
+
使用React hooks和函数组件`;
|
|
61
|
+
|
|
62
|
+
const result = parseMDC(content);
|
|
63
|
+
// result.frontmatter = { description: "React规则", globs: ["*.tsx", "*.jsx"], alwaysApply: true }
|
|
64
|
+
// result.content = "使用React hooks和函数组件"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### `serializeMDC(mdcContent: IMDCContent): string`
|
|
68
|
+
|
|
69
|
+
将 MDC 内容对象序列化为字符串格式。
|
|
70
|
+
|
|
71
|
+
**示例:**
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const mdcContent = {
|
|
75
|
+
frontmatter: {
|
|
76
|
+
description: 'TypeScript规则',
|
|
77
|
+
globs: ['*.ts'],
|
|
78
|
+
alwaysApply: false,
|
|
79
|
+
},
|
|
80
|
+
content: '使用严格的类型检查',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const serialized = serializeMDC(mdcContent);
|
|
84
|
+
// 输出:
|
|
85
|
+
// ---
|
|
86
|
+
// description: TypeScript规则
|
|
87
|
+
// globs:
|
|
88
|
+
// - "*.ts"
|
|
89
|
+
// alwaysApply: false
|
|
90
|
+
// ---
|
|
91
|
+
// 使用严格的类型检查
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `updateMDCFrontmatter(rawContent: string, newFrontmatter: Partial<IMDCFrontmatter>): string`
|
|
95
|
+
|
|
96
|
+
更新现有 MDC 内容的 frontmatter。
|
|
97
|
+
|
|
98
|
+
**示例:**
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const original = `---
|
|
102
|
+
description:
|
|
103
|
+
alwaysApply: false
|
|
104
|
+
---
|
|
105
|
+
原始内容`;
|
|
106
|
+
|
|
107
|
+
const updated = updateMDCFrontmatter(original, {
|
|
108
|
+
description: '更新后的描述',
|
|
109
|
+
globs: ['*.js'],
|
|
110
|
+
});
|
|
111
|
+
// 更新 frontmatter,保持原始内容不变
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `extractMDCContent(rawContent: string): string`
|
|
115
|
+
|
|
116
|
+
从 MDC 内容中提取纯文本内容(不包含 frontmatter)。
|
|
117
|
+
|
|
118
|
+
**示例:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const content = extractMDCContent(mdcContent);
|
|
122
|
+
// 只返回 Markdown 内容部分
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### `validateMDCFrontmatter(frontmatter: any): boolean`
|
|
126
|
+
|
|
127
|
+
验证 frontmatter 对象是否符合规范。
|
|
128
|
+
|
|
129
|
+
#### `createDefaultMDCFrontmatter(): IMDCFrontmatter`
|
|
130
|
+
|
|
131
|
+
创建默认的 frontmatter 对象。
|
|
132
|
+
|
|
133
|
+
## 在 RulesService 中的使用
|
|
134
|
+
|
|
135
|
+
`RulesService` 类已经集成了 MDC 解析功能:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// 解析全局规则
|
|
139
|
+
const mdcRules = rulesService.parseGlobalRulesAsMDC();
|
|
140
|
+
if (mdcRules) {
|
|
141
|
+
console.log('规则描述:', mdcRules.frontmatter.description);
|
|
142
|
+
console.log('适用文件:', mdcRules.frontmatter.globs);
|
|
143
|
+
console.log('规则内容:', mdcRules.content);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 更新全局规则
|
|
147
|
+
const newMDCContent = {
|
|
148
|
+
frontmatter: {
|
|
149
|
+
description: '新的编码规范',
|
|
150
|
+
globs: ['src/**/*.ts'],
|
|
151
|
+
alwaysApply: true,
|
|
152
|
+
},
|
|
153
|
+
content: '遵循 TypeScript 最佳实践',
|
|
154
|
+
};
|
|
155
|
+
rulesService.updateGlobalRulesWithMDC(newMDCContent);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 错误处理
|
|
159
|
+
|
|
160
|
+
解析器对错误具有良好的容错性:
|
|
161
|
+
|
|
162
|
+
- 如果 YAML frontmatter 格式错误,将返回空的 frontmatter 对象
|
|
163
|
+
- 如果没有 frontmatter,整个内容将作为 content 返回
|
|
164
|
+
- 序列化失败时,只返回内容部分
|
|
165
|
+
|
|
166
|
+
## 注意事项
|
|
167
|
+
|
|
168
|
+
1. frontmatter 中的 `globs` 字段可以是字符串或字符串数组
|
|
169
|
+
2. 所有 frontmatter 字段都是可选的
|
|
170
|
+
3. frontmatter 支持扩展,可以包含任意额外字段
|
|
171
|
+
4. 内容部分会自动去除首尾空白字符
|
package/src/common/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Event, URI } from '@opensumi/ide-core-common/lib/utils';
|
|
2
2
|
|
|
3
|
+
import { ProjectRule } from './types';
|
|
4
|
+
|
|
3
5
|
export interface LLMContextService {
|
|
4
6
|
/**
|
|
5
7
|
* 开始自动收集
|
|
@@ -21,6 +23,11 @@ export interface LLMContextService {
|
|
|
21
23
|
*/
|
|
22
24
|
addFolderToContext(uri: URI, isManual?: boolean): void;
|
|
23
25
|
|
|
26
|
+
/**
|
|
27
|
+
* 添加规则到 context 中
|
|
28
|
+
*/
|
|
29
|
+
addRuleToContext(uri: URI, isManual?: boolean): void;
|
|
30
|
+
|
|
24
31
|
/**
|
|
25
32
|
* 清除上下文
|
|
26
33
|
*/
|
|
@@ -33,6 +40,7 @@ export interface LLMContextService {
|
|
|
33
40
|
viewed: FileContext[];
|
|
34
41
|
attached: FileContext[];
|
|
35
42
|
attachedFolders: FileContext[];
|
|
43
|
+
attachedRules: ProjectRule[];
|
|
36
44
|
version: number;
|
|
37
45
|
}>;
|
|
38
46
|
|
|
@@ -42,6 +50,18 @@ export interface LLMContextService {
|
|
|
42
50
|
*/
|
|
43
51
|
removeFileFromContext(uri: URI, isManual?: boolean): void;
|
|
44
52
|
|
|
53
|
+
/**
|
|
54
|
+
* 从 context 中移除文件夹
|
|
55
|
+
* @param uri URI
|
|
56
|
+
*/
|
|
57
|
+
removeFolderFromContext(uri: URI): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 从 context 中移除规则
|
|
61
|
+
* @param uri URI
|
|
62
|
+
*/
|
|
63
|
+
removeRuleFromContext(uri: URI): void;
|
|
64
|
+
|
|
45
65
|
/** 导出为可序列化格式 */
|
|
46
66
|
serialize(): Promise<SerializedContext>;
|
|
47
67
|
}
|
|
@@ -58,12 +78,15 @@ export interface AttachFileContext {
|
|
|
58
78
|
lineErrors: string[];
|
|
59
79
|
path: string;
|
|
60
80
|
language: string;
|
|
81
|
+
selection?: [number, number];
|
|
61
82
|
}
|
|
62
83
|
|
|
63
84
|
export interface SerializedContext {
|
|
64
85
|
recentlyViewFiles: string[];
|
|
65
86
|
attachedFiles: Array<AttachFileContext>;
|
|
66
87
|
attachedFolders: string[];
|
|
88
|
+
attachedRules: string[];
|
|
89
|
+
globalRules: string[];
|
|
67
90
|
}
|
|
68
91
|
|
|
69
92
|
export enum LLM_CONTEXT_KEY {
|