@opensumi/ide-ai-native 3.9.1-next-1748593694.0 → 3.9.1-next-1748943529.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.
Files changed (118) hide show
  1. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  2. package/lib/browser/chat/chat-model.js +3 -1
  3. package/lib/browser/chat/chat-model.js.map +1 -1
  4. package/lib/browser/chat/chat-proxy.service.d.ts +0 -2
  5. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  6. package/lib/browser/chat/chat-proxy.service.js +50 -57
  7. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  8. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  9. package/lib/browser/chat/chat.view.js +11 -7
  10. package/lib/browser/chat/chat.view.js.map +1 -1
  11. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  12. package/lib/browser/components/ChatMentionInput.js +123 -27
  13. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  14. package/lib/browser/components/ChatToolRender.module.less +1 -0
  15. package/lib/browser/components/components.module.less +7 -0
  16. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  17. package/lib/browser/components/mention-input/mention-input.js +155 -13
  18. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  19. package/lib/browser/components/mention-input/mention-input.module.less +165 -0
  20. package/lib/browser/components/mention-input/types.d.ts +15 -1
  21. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  22. package/lib/browser/components/mention-input/types.js +1 -0
  23. package/lib/browser/components/mention-input/types.js.map +1 -1
  24. package/lib/browser/context/llm-context.service.d.ts +21 -2
  25. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  26. package/lib/browser/context/llm-context.service.js +162 -20
  27. package/lib/browser/context/llm-context.service.js.map +1 -1
  28. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
  29. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
  30. package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
  31. package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
  32. package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
  33. package/lib/browser/index.d.ts.map +1 -1
  34. package/lib/browser/index.js +7 -0
  35. package/lib/browser/index.js.map +1 -1
  36. package/lib/browser/mcp/base-apply.service.d.ts +2 -2
  37. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  38. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  39. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  40. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  41. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  42. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  43. package/lib/browser/preferences/schema.d.ts.map +1 -1
  44. package/lib/browser/preferences/schema.js +5 -0
  45. package/lib/browser/preferences/schema.js.map +1 -1
  46. package/lib/browser/rules/rules.contribution.d.ts +29 -0
  47. package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
  48. package/lib/browser/rules/rules.contribution.js +94 -0
  49. package/lib/browser/rules/rules.contribution.js.map +1 -0
  50. package/lib/browser/rules/rules.module.less +174 -0
  51. package/lib/browser/rules/rules.service.d.ts +25 -0
  52. package/lib/browser/rules/rules.service.d.ts.map +1 -0
  53. package/lib/browser/rules/rules.service.js +180 -0
  54. package/lib/browser/rules/rules.service.js.map +1 -0
  55. package/lib/browser/rules/rules.view.d.ts +3 -0
  56. package/lib/browser/rules/rules.view.d.ts.map +1 -0
  57. package/lib/browser/rules/rules.view.js +76 -0
  58. package/lib/browser/rules/rules.view.js.map +1 -0
  59. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  60. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  61. package/lib/common/MDC_PARSER_README.md +171 -0
  62. package/lib/common/index.d.ts +2 -0
  63. package/lib/common/index.d.ts.map +1 -1
  64. package/lib/common/index.js +2 -0
  65. package/lib/common/index.js.map +1 -1
  66. package/lib/common/llm-context.d.ts +19 -0
  67. package/lib/common/llm-context.d.ts.map +1 -1
  68. package/lib/common/llm-context.js.map +1 -1
  69. package/lib/common/mdc-parser.d.ts +60 -0
  70. package/lib/common/mdc-parser.d.ts.map +1 -0
  71. package/lib/common/mdc-parser.js +246 -0
  72. package/lib/common/mdc-parser.js.map +1 -0
  73. package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
  74. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  75. package/lib/common/prompts/context-prompt-provider.js +35 -29
  76. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  77. package/lib/common/prompts/system-prompt.d.ts +2 -0
  78. package/lib/common/prompts/system-prompt.d.ts.map +1 -0
  79. package/lib/common/prompts/system-prompt.js +5 -0
  80. package/lib/common/prompts/system-prompt.js.map +1 -0
  81. package/lib/common/types.d.ts +7 -0
  82. package/lib/common/types.d.ts.map +1 -1
  83. package/lib/node/base-language-model.d.ts.map +1 -1
  84. package/lib/node/base-language-model.js.map +1 -1
  85. package/package.json +25 -24
  86. package/src/browser/chat/chat-model.ts +3 -1
  87. package/src/browser/chat/chat-proxy.service.ts +68 -81
  88. package/src/browser/chat/chat.view.tsx +19 -7
  89. package/src/browser/components/ChatMentionInput.tsx +143 -31
  90. package/src/browser/components/ChatToolRender.module.less +1 -0
  91. package/src/browser/components/components.module.less +7 -0
  92. package/src/browser/components/mention-input/mention-input.module.less +165 -0
  93. package/src/browser/components/mention-input/mention-input.tsx +244 -29
  94. package/src/browser/components/mention-input/types.ts +15 -0
  95. package/src/browser/context/llm-context.service.ts +182 -21
  96. package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
  97. package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
  98. package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
  99. package/src/browser/index.ts +8 -0
  100. package/src/browser/mcp/base-apply.service.ts +0 -1
  101. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
  102. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
  103. package/src/browser/mcp/tools/handlers/ListDir.ts +1 -1
  104. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
  105. package/src/browser/preferences/schema.ts +5 -0
  106. package/src/browser/rules/rules.contribution.ts +105 -0
  107. package/src/browser/rules/rules.module.less +174 -0
  108. package/src/browser/rules/rules.service.ts +189 -0
  109. package/src/browser/rules/rules.view.tsx +127 -0
  110. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
  111. package/src/common/MDC_PARSER_README.md +171 -0
  112. package/src/common/index.ts +3 -0
  113. package/src/common/llm-context.ts +23 -0
  114. package/src/common/mdc-parser.ts +295 -0
  115. package/src/common/prompts/context-prompt-provider.ts +55 -40
  116. package/src/common/prompts/system-prompt.ts +2 -0
  117. package/src/common/types.ts +8 -0
  118. package/src/node/base-language-model.ts +0 -1
@@ -0,0 +1,174 @@
1
+ .container {
2
+ padding: 20px;
3
+ color: var(--foreground);
4
+ textarea {
5
+ resize: vertical;
6
+ min-height: 60px;
7
+ }
8
+ input,
9
+ textarea {
10
+ width: 100%;
11
+ padding: 8px;
12
+ border: 1px solid var(--input-border);
13
+ border-radius: 4px;
14
+ background-color: var(--input-background);
15
+ color: var(--input-foreground);
16
+ font-size: 13px;
17
+ font-family: var(--monaco-monospace-font);
18
+
19
+ &:focus {
20
+ border-color: var(--focusBorder);
21
+ outline: none;
22
+ }
23
+
24
+ &::placeholder {
25
+ color: var(--descriptionForeground);
26
+ opacity: 0.6;
27
+ }
28
+ }
29
+ }
30
+
31
+ .header {
32
+ display: flex;
33
+ justify-content: space-between;
34
+ align-items: flex-start;
35
+ margin-bottom: 24px;
36
+ }
37
+
38
+ .title {
39
+ margin: 0 0 8px 0;
40
+ font-size: 24px;
41
+ font-weight: 600;
42
+ }
43
+
44
+ .description {
45
+ margin: 0;
46
+ color: var(--descriptionForeground);
47
+ font-size: 14px;
48
+ line-height: 1.5;
49
+ }
50
+
51
+ .user_rules {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 16px;
55
+ .user_rules_header {
56
+ display: flex;
57
+ flex-direction: column;
58
+ }
59
+ }
60
+
61
+ .project_rules {
62
+ margin-top: 20px;
63
+ display: flex;
64
+ flex-direction: column;
65
+ gap: 16px;
66
+ h3 {
67
+ position: relative;
68
+ }
69
+ .actionButton {
70
+ position: absolute;
71
+ right: 0;
72
+ top: 0;
73
+ }
74
+ }
75
+
76
+ .project_rules_header {
77
+ display: flex;
78
+ justify-content: space-between;
79
+ align-items: flex-start;
80
+ }
81
+
82
+ .project_rules_header_left {
83
+ display: flex;
84
+ flex-direction: column;
85
+ }
86
+
87
+ .actionButton {
88
+ padding: 8px 16px;
89
+ border-radius: 4px;
90
+ background-color: var(--button-primary-background);
91
+ color: var(--button-primary-foreground);
92
+ border: none;
93
+ cursor: pointer;
94
+ font-size: 13px;
95
+ .actionButtonIcon {
96
+ margin-right: 5px;
97
+ }
98
+
99
+ &:hover {
100
+ background-color: var(--button-primary-hover-background);
101
+ }
102
+ }
103
+
104
+ .project_rules_list {
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: 8px;
108
+ }
109
+
110
+ .rule_item {
111
+ display: flex;
112
+ justify-content: space-between;
113
+ align-items: center;
114
+ padding: 12px;
115
+ border: 1px solid var(--input-border);
116
+ border-radius: 4px;
117
+ background-color: var(--input-background);
118
+ opacity: 0.6;
119
+
120
+ &:hover {
121
+ opacity: 1;
122
+ }
123
+ }
124
+
125
+ .rule_item_left {
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: 4px;
129
+ flex: 1;
130
+ }
131
+
132
+ .rule_item_right {
133
+ display: flex;
134
+ align-items: center;
135
+ flex-shrink: 0;
136
+ margin-left: 16px;
137
+ }
138
+
139
+ .rule_filename {
140
+ font-size: 13px;
141
+ font-weight: 500;
142
+ color: var(--foreground);
143
+ }
144
+
145
+ .rule_description {
146
+ font-size: 12px;
147
+ color: var(--descriptionForeground);
148
+ }
149
+
150
+ .rule_warning {
151
+ font-size: 12px;
152
+ color: var(--editorWarning-foreground);
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 4px;
156
+
157
+ &::before {
158
+ content: '⚠';
159
+ color: var(--editorWarning-foreground);
160
+ }
161
+ }
162
+
163
+ .rule_applies_to {
164
+ font-size: 12px;
165
+ color: var(--descriptionForeground);
166
+ font-family: var(--monaco-monospace-font);
167
+ }
168
+
169
+ .empty_state {
170
+ text-align: center;
171
+ padding: 32px;
172
+ color: var(--descriptionForeground);
173
+ font-style: italic;
174
+ }
@@ -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) => 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
+ };
@@ -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. 内容部分会自动去除首尾空白字符
@@ -333,3 +333,6 @@ export interface IInlineDiffService {
333
333
  }
334
334
 
335
335
  export const InlineDiffServiceToken = Symbol('InlineDiffService');
336
+
337
+ export * from './tool-invocation-registry';
338
+ export * from './mdc-parser';