@opensumi/ide-ai-native 3.8.1-next-1741080291.0 → 3.8.1-next-1741091353.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 (192) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +0 -3
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +1 -45
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-agent.service.d.ts +8 -0
  6. package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-agent.service.js +32 -5
  8. package/lib/browser/chat/chat-agent.service.js.map +1 -1
  9. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  10. package/lib/browser/chat/chat-manager.service.js +4 -0
  11. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  12. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  13. package/lib/browser/chat/chat-model.js +3 -2
  14. package/lib/browser/chat/chat-model.js.map +1 -1
  15. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  16. package/lib/browser/chat/chat-proxy.service.js +5 -9
  17. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  18. package/lib/browser/chat/chat.module.less +1 -2
  19. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  20. package/lib/browser/chat/chat.view.js +13 -39
  21. package/lib/browser/chat/chat.view.js.map +1 -1
  22. package/lib/browser/components/ChatContext/index.js +2 -2
  23. package/lib/browser/components/ChatContext/index.js.map +1 -1
  24. package/lib/browser/components/ChatInput.d.ts.map +1 -1
  25. package/lib/browser/components/ChatInput.js +25 -1
  26. package/lib/browser/components/ChatInput.js.map +1 -1
  27. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  28. package/lib/browser/components/ChatToolRender.js +2 -3
  29. package/lib/browser/components/ChatToolRender.js.map +1 -1
  30. package/lib/browser/components/chat-history.module.less +2 -1
  31. package/lib/browser/components/components.module.less +20 -0
  32. package/lib/browser/context/llm-context.service.d.ts +18 -5
  33. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  34. package/lib/browser/context/llm-context.service.js +80 -47
  35. package/lib/browser/context/llm-context.service.js.map +1 -1
  36. package/lib/browser/layout/layout.module.less +4 -4
  37. package/lib/browser/mcp/base-apply.service.d.ts +18 -7
  38. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  39. package/lib/browser/mcp/base-apply.service.js +185 -65
  40. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  41. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
  42. package/lib/browser/mcp/tools/components/EditFile.js +15 -9
  43. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
  44. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts +13 -0
  45. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts.map +1 -0
  46. package/lib/browser/mcp/tools/components/{SearchResult.js → ExpandableFileList.js} +29 -19
  47. package/lib/browser/mcp/tools/components/ExpandableFileList.js.map +1 -0
  48. package/lib/browser/mcp/tools/components/index.module.less +4 -0
  49. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +1 -0
  50. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  51. package/lib/browser/mcp/tools/createNewFileWithText.js +19 -11
  52. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  53. package/lib/browser/mcp/tools/fileSearch.d.ts +1 -0
  54. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
  55. package/lib/browser/mcp/tools/fileSearch.js +14 -5
  56. package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
  57. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  58. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -0
  59. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  60. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
  61. package/lib/browser/mcp/tools/grepSearch.js +6 -3
  62. package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
  63. package/lib/browser/mcp/tools/handlers/EditFile.js +1 -1
  64. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
  65. package/lib/browser/mcp/tools/handlers/ListDir.d.ts +1 -0
  66. package/lib/browser/mcp/tools/handlers/ListDir.d.ts.map +1 -1
  67. package/lib/browser/mcp/tools/handlers/ListDir.js +3 -0
  68. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  69. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  70. package/lib/browser/mcp/tools/handlers/RunCommand.js +2 -0
  71. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  72. package/lib/browser/mcp/tools/listDir.d.ts +1 -0
  73. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  74. package/lib/browser/mcp/tools/listDir.js +35 -4
  75. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  76. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  77. package/lib/browser/mcp/tools/runTerminalCmd.js +1 -0
  78. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  79. package/lib/browser/model/msg-history-manager.d.ts +1 -0
  80. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  81. package/lib/browser/model/msg-history-manager.js +3 -0
  82. package/lib/browser/model/msg-history-manager.js.map +1 -1
  83. package/lib/browser/preferences/schema.d.ts.map +1 -1
  84. package/lib/browser/preferences/schema.js +1 -7
  85. package/lib/browser/preferences/schema.js.map +1 -1
  86. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -1
  87. package/lib/browser/widget/inline-diff/inline-diff-manager.js +68 -8
  88. package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
  89. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +10 -4
  90. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
  91. package/lib/browser/widget/inline-diff/inline-diff-previewer.js +14 -3
  92. package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
  93. package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  94. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +3 -3
  95. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  96. package/lib/browser/widget/inline-diff/inline-diff.controller.js +10 -5
  97. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  98. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +46 -17
  99. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  100. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +110 -53
  101. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  102. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +4 -0
  103. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  104. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +26 -1
  105. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  106. package/lib/common/index.d.ts +0 -1
  107. package/lib/common/index.d.ts.map +1 -1
  108. package/lib/common/index.js +0 -2
  109. package/lib/common/index.js.map +1 -1
  110. package/lib/common/llm-context.d.ts +13 -9
  111. package/lib/common/llm-context.d.ts.map +1 -1
  112. package/lib/common/llm-context.js.map +1 -1
  113. package/lib/common/prompts/context-prompt-provider.d.ts +4 -3
  114. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  115. package/lib/common/prompts/context-prompt-provider.js +33 -22
  116. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  117. package/lib/common/types.d.ts +1 -0
  118. package/lib/common/types.d.ts.map +1 -1
  119. package/lib/node/anthropic/anthropic-language-model.d.ts +1 -3
  120. package/lib/node/anthropic/anthropic-language-model.d.ts.map +1 -1
  121. package/lib/node/anthropic/anthropic-language-model.js +2 -6
  122. package/lib/node/anthropic/anthropic-language-model.js.map +1 -1
  123. package/lib/node/base-language-model.d.ts +1 -4
  124. package/lib/node/base-language-model.d.ts.map +1 -1
  125. package/lib/node/base-language-model.js +6 -7
  126. package/lib/node/base-language-model.js.map +1 -1
  127. package/lib/node/deepseek/deepseek-language-model.d.ts +1 -3
  128. package/lib/node/deepseek/deepseek-language-model.d.ts.map +1 -1
  129. package/lib/node/deepseek/deepseek-language-model.js +2 -6
  130. package/lib/node/deepseek/deepseek-language-model.js.map +1 -1
  131. package/lib/node/openai/openai-language-model.d.ts +4 -5
  132. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  133. package/lib/node/openai/openai-language-model.js +7 -8
  134. package/lib/node/openai/openai-language-model.js.map +1 -1
  135. package/package.json +23 -24
  136. package/src/browser/ai-core.contribution.ts +1 -56
  137. package/src/browser/chat/chat-agent.service.ts +38 -7
  138. package/src/browser/chat/chat-manager.service.ts +6 -0
  139. package/src/browser/chat/chat-model.ts +11 -6
  140. package/src/browser/chat/chat-proxy.service.ts +6 -9
  141. package/src/browser/chat/chat.module.less +1 -2
  142. package/src/browser/chat/chat.view.tsx +14 -72
  143. package/src/browser/components/ChatContext/index.tsx +2 -2
  144. package/src/browser/components/ChatInput.tsx +67 -3
  145. package/src/browser/components/ChatToolRender.tsx +1 -2
  146. package/src/browser/components/chat-history.module.less +2 -1
  147. package/src/browser/components/components.module.less +20 -0
  148. package/src/browser/context/llm-context.service.ts +93 -54
  149. package/src/browser/layout/layout.module.less +4 -4
  150. package/src/browser/mcp/base-apply.service.ts +222 -67
  151. package/src/browser/mcp/tools/components/EditFile.tsx +16 -9
  152. package/src/browser/mcp/tools/components/ExpandableFileList.tsx +133 -0
  153. package/src/browser/mcp/tools/components/index.module.less +4 -0
  154. package/src/browser/mcp/tools/createNewFileWithText.ts +21 -12
  155. package/src/browser/mcp/tools/fileSearch.ts +14 -4
  156. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -0
  157. package/src/browser/mcp/tools/grepSearch.ts +6 -3
  158. package/src/browser/mcp/tools/handlers/EditFile.ts +1 -1
  159. package/src/browser/mcp/tools/handlers/ListDir.ts +4 -0
  160. package/src/browser/mcp/tools/handlers/RunCommand.ts +2 -0
  161. package/src/browser/mcp/tools/listDir.ts +36 -5
  162. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -0
  163. package/src/browser/model/msg-history-manager.ts +4 -0
  164. package/src/browser/preferences/schema.ts +1 -7
  165. package/src/browser/widget/inline-diff/inline-diff-manager.tsx +143 -21
  166. package/src/browser/widget/inline-diff/inline-diff-previewer.ts +25 -7
  167. package/src/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  168. package/src/browser/widget/inline-diff/inline-diff.controller.ts +16 -8
  169. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +139 -68
  170. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +30 -1
  171. package/src/common/index.ts +0 -2
  172. package/src/common/llm-context.ts +10 -4
  173. package/src/common/prompts/context-prompt-provider.ts +38 -29
  174. package/src/common/types.ts +1 -0
  175. package/src/node/anthropic/anthropic-language-model.ts +2 -7
  176. package/src/node/base-language-model.ts +12 -10
  177. package/src/node/deepseek/deepseek-language-model.ts +2 -7
  178. package/src/node/openai/openai-language-model.ts +9 -10
  179. package/lib/browser/mcp/tools/components/SearchResult.d.ts +0 -11
  180. package/lib/browser/mcp/tools/components/SearchResult.d.ts.map +0 -1
  181. package/lib/browser/mcp/tools/components/SearchResult.js.map +0 -1
  182. package/lib/common/model.d.ts +0 -12
  183. package/lib/common/model.d.ts.map +0 -1
  184. package/lib/common/model.js +0 -83
  185. package/lib/common/model.js.map +0 -1
  186. package/lib/node/openai-compatible/openai-compatible-language-model.d.ts +0 -10
  187. package/lib/node/openai-compatible/openai-compatible-language-model.d.ts.map +0 -1
  188. package/lib/node/openai-compatible/openai-compatible-language-model.js +0 -32
  189. package/lib/node/openai-compatible/openai-compatible-language-model.js.map +0 -1
  190. package/src/browser/mcp/tools/components/SearchResult.tsx +0 -92
  191. package/src/common/model.ts +0 -90
  192. package/src/node/openai-compatible/openai-compatible-language-model.ts +0 -30
@@ -0,0 +1,133 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+
3
+ import { CommandService, LabelService, URI, path, useInjectable } from '@opensumi/ide-core-browser';
4
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
5
+ import { IWorkspaceService } from '@opensumi/ide-workspace';
6
+
7
+ import { IChatInternalService } from '../../../../common';
8
+ import { ChatInternalService } from '../../../chat/chat.internal.service';
9
+
10
+ import styles from './index.module.less';
11
+
12
+ interface FileInfo {
13
+ uri: string;
14
+ isDirectory: boolean;
15
+ }
16
+
17
+ interface ExpandableFileListProps {
18
+ args: any;
19
+ toolCallId: string;
20
+ messageId: string;
21
+ toolName: string;
22
+ headerText?: string;
23
+ }
24
+
25
+ export const FileSearchToolComponent: React.FC<ExpandableFileListProps> = ({ args, toolCallId, messageId }) => (
26
+ <ExpandableFileList
27
+ args={args}
28
+ toolCallId={toolCallId}
29
+ messageId={messageId}
30
+ toolName='fileSearch'
31
+ headerText={`Searched files "${args.query}"`}
32
+ />
33
+ );
34
+
35
+ export const GrepSearchToolComponent: React.FC<ExpandableFileListProps> = ({ args, toolCallId, messageId }) => (
36
+ <ExpandableFileList
37
+ args={args}
38
+ toolCallId={toolCallId}
39
+ messageId={messageId}
40
+ toolName='grepSearch'
41
+ headerText={`Grepped codebase "${args.query}"`}
42
+ />
43
+ );
44
+
45
+ export const ListDirToolComponent: React.FC<ExpandableFileListProps> = ({ args, toolCallId, messageId }) => (
46
+ <ExpandableFileList
47
+ args={args}
48
+ toolCallId={toolCallId}
49
+ messageId={messageId}
50
+ toolName='listDir'
51
+ headerText={`Listed directory "${args.relative_workspace_path}"`}
52
+ />
53
+ );
54
+
55
+ const ExpandableFileList: React.FC<ExpandableFileListProps> = ({
56
+ args,
57
+ toolCallId,
58
+ toolName,
59
+ messageId,
60
+ headerText,
61
+ }) => {
62
+ const [isExpanded, setIsExpanded] = useState(false);
63
+ const labelService = useInjectable<LabelService>(LabelService);
64
+ const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
65
+ const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
66
+ const commandService = useInjectable<CommandService>(CommandService);
67
+ const workspaceRoot = useMemo(() => URI.parse(workspaceService.tryGetRoots()?.[0]?.uri), []);
68
+
69
+ const chatService = useInjectable<ChatInternalService>(IChatInternalService);
70
+ const [fileList, setFileList] = useState<FileInfo[]>([]);
71
+
72
+ useEffect(() => {
73
+ const toDispose = chatService.sessionModel.history.onMessageAdditionalChange((additional) => {
74
+ setFileList(additional[toolCallId]?.files || []);
75
+ });
76
+ return () => {
77
+ toDispose.dispose();
78
+ };
79
+ }, []);
80
+
81
+ const handleFileClick = async (fileInfo: FileInfo) => {
82
+ // 处理文件点击跳转
83
+ if (!fileInfo.isDirectory) {
84
+ editorService.open(URI.parse(fileInfo.uri));
85
+ } else {
86
+ // 如果是目录,聚焦到文件树并展开该目录
87
+ const uri = URI.parse(fileInfo.uri);
88
+ commandService.executeCommand('filetree.location', uri);
89
+ }
90
+ };
91
+
92
+ const parsedFiles = useMemo(
93
+ () =>
94
+ fileList.map((file) => {
95
+ const uri = URI.parse(file.uri);
96
+ const iconClass = labelService.getIcon(uri, { isDirectory: file.isDirectory });
97
+ return {
98
+ iconClass,
99
+ name: uri.path.base,
100
+ path: path.relative(workspaceRoot.codeUri.fsPath, uri.path.dir.toString()),
101
+ uri: file.uri,
102
+ isDirectory: file.isDirectory,
103
+ };
104
+ }),
105
+ [fileList],
106
+ );
107
+
108
+ return (
109
+ <div className={styles.container}>
110
+ <div className={styles.header} onClick={() => setIsExpanded(!isExpanded)}>
111
+ <span style={{ transform: `rotate(${isExpanded ? '90deg' : '0deg'})` }}>▶</span>
112
+ <span>
113
+ {headerText} · {fileList.length} files
114
+ </span>
115
+ </div>
116
+ {isExpanded && (
117
+ <ul className={styles.fileList}>
118
+ {parsedFiles.map((file, index) => (
119
+ <li
120
+ key={index}
121
+ className={styles.fileItem}
122
+ onClick={() => handleFileClick({ uri: file.uri, isDirectory: file.isDirectory })}
123
+ >
124
+ <span className={file.iconClass}></span>
125
+ <span style={{ flex: 1 }}>{file.name}</span>
126
+ <span className={styles.filePath}>{file.path}</span>
127
+ </li>
128
+ ))}
129
+ </ul>
130
+ )}
131
+ </div>
132
+ );
133
+ };
@@ -17,6 +17,9 @@
17
17
  align-items: center;
18
18
  justify-content: center;
19
19
  }
20
+ .left {
21
+ overflow: hidden;
22
+ }
20
23
  .left,
21
24
  .right {
22
25
  display: flex;
@@ -154,6 +157,7 @@
154
157
  background-color: var(--design-chatInput-background);
155
158
  padding: 10px;
156
159
  border-radius: 4px;
160
+ margin: 10px 0px;
157
161
 
158
162
  .command_title {
159
163
  display: flex;
@@ -6,10 +6,13 @@ import { IFileServiceClient } from '@opensumi/ide-file-service';
6
6
  import { IWorkspaceService } from '@opensumi/ide-workspace';
7
7
 
8
8
  import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
9
+ import { BaseApplyService } from '../base-apply.service';
10
+
11
+ import { EditFileToolComponent } from './components/EditFile';
9
12
 
10
13
  const inputSchema = z.object({
11
- pathInProject: z.string().describe('The relative path where the file should be created'),
12
- text: z.string().describe('The content to write into the new file'),
14
+ target_file: z.string().describe('The relative path where the file should be created'),
15
+ code_edit: z.string().describe('The content to write into the new file'),
13
16
  });
14
17
 
15
18
  @Domain(MCPServerContribution)
@@ -20,29 +23,31 @@ export class CreateNewFileWithTextTool implements MCPServerContribution {
20
23
  @Autowired(IFileServiceClient)
21
24
  private readonly fileService: IFileServiceClient;
22
25
 
26
+ @Autowired(BaseApplyService)
27
+ private applyService: BaseApplyService;
28
+
23
29
  registerMCPServer(registry: IMCPServerRegistry): void {
24
30
  registry.registerMCPTool(this.getToolDefinition());
31
+ registry.registerToolComponent('create_new_file_with_text', EditFileToolComponent);
25
32
  }
26
33
 
27
34
  getToolDefinition(): MCPToolDefinition {
28
35
  return {
29
36
  name: 'create_new_file_with_text',
37
+ label: 'Create File',
30
38
  description:
31
39
  'Creates a new file at the specified path within the project directory and populates it with the provided text. ' +
32
40
  'Use this tool to generate new files in your project structure. ' +
33
- 'Requires two parameters: ' +
34
- '- pathInProject: The relative path where the file should be created ' +
35
- '- text: The content to write into the new file ' +
36
41
  'Returns one of two possible responses: ' +
37
42
  '"ok" if the file was successfully created and populated, ' +
38
43
  '"can\'t find project dir" if the project directory cannot be determined. ' +
39
- 'Note: Creates any necessary parent directories automatically.',
44
+ 'Note: This tool creates any necessary parent directories automatically.',
40
45
  inputSchema,
41
46
  handler: this.handler.bind(this),
42
47
  };
43
48
  }
44
49
 
45
- private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
50
+ private async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
46
51
  try {
47
52
  // 获取工作区根目录
48
53
  const workspaceRoots = this.workspaceService.tryGetRoots();
@@ -56,7 +61,7 @@ export class CreateNewFileWithTextTool implements MCPServerContribution {
56
61
 
57
62
  // 构建完整的文件路径
58
63
  const rootUri = URI.parse(workspaceRoots[0].uri);
59
- const fullPath = path.join(rootUri.codeUri.fsPath, args.pathInProject);
64
+ const fullPath = path.join(rootUri.codeUri.fsPath, args.target_file);
60
65
  const fileUri = URI.file(fullPath);
61
66
 
62
67
  // 创建父目录
@@ -64,17 +69,21 @@ export class CreateNewFileWithTextTool implements MCPServerContribution {
64
69
  const parentUri = URI.file(parentDir);
65
70
  await this.fileService.createFolder(parentUri.toString());
66
71
 
67
- // 写入文件内容
68
- await this.fileService.createFile(fileUri.toString(), { content: args.text });
72
+ // 创建文件
73
+ await this.fileService.createFile(fileUri.toString());
74
+
75
+ // 使用 applyService 写入文件内容
76
+ const codeBlock = await this.applyService.registerCodeBlock(args.target_file, args.code_edit, args.toolCallId);
77
+ await this.applyService.apply(codeBlock);
69
78
 
70
- logger.appendLine(`Successfully created file at: ${args.pathInProject}`);
79
+ logger.appendLine(`Successfully created file at: ${args.target_file}`);
71
80
  return {
72
81
  content: [{ type: 'text', text: 'ok' }],
73
82
  };
74
83
  } catch (error) {
75
84
  logger.appendLine(`Error during file creation: ${error}`);
76
85
  return {
77
- content: [{ type: 'text', text: 'unknown error' }],
86
+ content: [{ type: 'text', text: error.message }],
78
87
  isError: true,
79
88
  };
80
89
  }
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  import { Autowired } from '@opensumi/di';
4
+ import { getValidateInput } from '@opensumi/ide-addons/lib/browser/file-search.contribution';
4
5
  import { Domain, URI } from '@opensumi/ide-core-common';
5
6
  import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search/lib/common';
6
7
  import { IWorkspaceService } from '@opensumi/ide-workspace';
@@ -9,7 +10,7 @@ import { IChatInternalService } from '../../../common';
9
10
  import { ChatInternalService } from '../../chat/chat.internal.service';
10
11
  import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
11
12
 
12
- import { FileSearchToolComponent } from './components/SearchResult';
13
+ import { FileSearchToolComponent } from './components/ExpandableFileList';
13
14
 
14
15
  const inputSchema = z.object({
15
16
  query: z.string().describe('Fuzzy filename to search for'),
@@ -58,7 +59,7 @@ export class FileSearchTool implements MCPServerContribution {
58
59
  }
59
60
 
60
61
  // 使用 OpenSumi 的文件搜索 API
61
- const searchPattern = args.query;
62
+ const searchPattern = this.normalizeQuery(args.query);
62
63
  const searchResults = await this.fileSearchService.find(searchPattern, {
63
64
  rootUris: [new URI(workspaceRoots[0].uri).codeUri.fsPath],
64
65
  // TODO: 忽略配置
@@ -71,7 +72,10 @@ export class FileSearchTool implements MCPServerContribution {
71
72
 
72
73
  const files = searchResults.slice(0, MAX_RESULTS).map((file) => {
73
74
  const uri = URI.parse(file);
74
- return uri.codeUri.fsPath;
75
+ return {
76
+ uri: uri.codeUri.fsPath,
77
+ isDirectory: false, // 文件搜索结果都是文件
78
+ };
75
79
  });
76
80
 
77
81
  const messages = this.chatInternalService.sessionModel.history.getMessages();
@@ -87,7 +91,7 @@ export class FileSearchTool implements MCPServerContribution {
87
91
  content: [
88
92
  {
89
93
  type: 'text',
90
- text: `${files.join('\n')}\n${
94
+ text: `${files.map((f) => f.uri).join('\n')}\n${
91
95
  searchResults.length > MAX_RESULTS
92
96
  ? `\nFound ${searchResults.length} files matching "${args.query}", only return the first ${MAX_RESULTS} results`
93
97
  : ''
@@ -96,4 +100,10 @@ export class FileSearchTool implements MCPServerContribution {
96
100
  ],
97
101
  };
98
102
  }
103
+
104
+ private normalizeQuery(query: string): string {
105
+ const nonBlank = query.trim().replace(/\s/g, '');
106
+ const validated = getValidateInput(nonBlank);
107
+ return validated;
108
+ }
99
109
  }
@@ -29,6 +29,7 @@ export class GetDiagnosticsByPathTool implements MCPServerContribution {
29
29
  getToolDefinition(): MCPToolDefinition {
30
30
  return {
31
31
  name: 'get_diagnostics_by_path',
32
+ label: 'Get Diagnostics',
32
33
  description:
33
34
  'Retrieves diagnostic information (errors, warnings, etc.) from a specific file in the project. ' +
34
35
  'Use this tool to get information about problems in any project file. ' +
@@ -10,7 +10,7 @@ import { IChatInternalService } from '../../../common';
10
10
  import { ChatInternalService } from '../../chat/chat.internal.service';
11
11
  import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
12
12
 
13
- import { GrepSearchToolComponent } from './components/SearchResult';
13
+ import { GrepSearchToolComponent } from './components/ExpandableFileList';
14
14
 
15
15
  const inputSchema = z.object({
16
16
  query: z.string().describe('The regex pattern to search for'),
@@ -90,7 +90,7 @@ export class GrepSearchTool implements MCPServerContribution {
90
90
  return;
91
91
  }
92
92
  const results: string[] = [];
93
- const files: string[] = [];
93
+ const files: Array<{ uri: string; isDirectory: boolean }> = [];
94
94
  for (const [fileUri, result] of this.searchService.searchResults.entries()) {
95
95
  results.push(
96
96
  `File: ${fileUri}\n${result
@@ -103,7 +103,10 @@ export class GrepSearchTool implements MCPServerContribution {
103
103
  .map((r) => `Line: ${r.line}\nContent: ${r.lineText || r.renderLineText}`)
104
104
  .join('\n')}`,
105
105
  );
106
- files.push(fileUri);
106
+ files.push({
107
+ uri: fileUri,
108
+ isDirectory: false, // grep 搜索结果都是文件
109
+ });
107
110
  }
108
111
  deferred.resolve(results.join('\n\n'));
109
112
  const messages = this.chatInternalService.sessionModel.history.getMessages();
@@ -14,7 +14,7 @@ export class EditFileHandler {
14
14
 
15
15
  async handler(params: { targetFile: string; codeEdit: string; instructions?: string }, toolCallId: string) {
16
16
  const { targetFile, codeEdit } = params;
17
- const block = this.applyService.registerCodeBlock(targetFile, codeEdit, toolCallId);
17
+ const block = await this.applyService.registerCodeBlock(targetFile, codeEdit, toolCallId);
18
18
  const blockData = await this.applyService.apply(block);
19
19
  return blockData;
20
20
  }
@@ -56,6 +56,10 @@ export class ListDirHandler {
56
56
  @Autowired(IFileServiceClient)
57
57
  private readonly fileSystemService: IFileServiceClient;
58
58
 
59
+ getWorkspaceDir(): string {
60
+ return this.appConfig.workspaceDir;
61
+ }
62
+
59
63
  async handler(args: { relativeWorkspacePath: string }) {
60
64
  const { relativeWorkspacePath } = args;
61
65
  if (!relativeWorkspacePath) {
@@ -49,6 +49,7 @@ export class RunCommandHandler {
49
49
  }
50
50
 
51
51
  async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
52
+ logger.appendLine(`Executing command: ${args.command}`);
52
53
  if (args.require_user_approval) {
53
54
  const def = new Deferred<boolean>();
54
55
  this.approvalDeferredMap.set(args.toolCallId, def);
@@ -89,6 +90,7 @@ export class RunCommandHandler {
89
90
  content: result,
90
91
  });
91
92
 
93
+ logger.appendLine(`Command ${args.command} finished with exit code: ${e.code}`);
92
94
  terminalClient.term.writeln(
93
95
  `\n${color.italic}> Command ${args.command} executed successfully. Terminal will close in ${
94
96
  3000 / 1000
@@ -3,8 +3,11 @@ import { z } from 'zod';
3
3
  import { Autowired } from '@opensumi/di';
4
4
  import { Domain } from '@opensumi/ide-core-common';
5
5
 
6
+ import { IChatInternalService } from '../../../common';
7
+ import { ChatInternalService } from '../../chat/chat.internal.service';
6
8
  import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
7
9
 
10
+ import { ListDirToolComponent } from './components/ExpandableFileList';
8
11
  import { ListDirHandler } from './handlers/ListDir';
9
12
 
10
13
  const inputSchema = z
@@ -26,8 +29,12 @@ export class ListDirTool implements MCPServerContribution {
26
29
  @Autowired(ListDirHandler)
27
30
  private readonly listDirHandler: ListDirHandler;
28
31
 
32
+ @Autowired(IChatInternalService)
33
+ private readonly chatInternalService: ChatInternalService;
34
+
29
35
  registerMCPServer(registry: IMCPServerRegistry): void {
30
36
  registry.registerMCPTool(this.getToolDefinition());
37
+ registry.registerToolComponent('list_dir', ListDirToolComponent);
31
38
  }
32
39
 
33
40
  getToolDefinition(): MCPToolDefinition {
@@ -41,16 +48,40 @@ export class ListDirTool implements MCPServerContribution {
41
48
  };
42
49
  }
43
50
 
44
- private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
51
+ private async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
45
52
  const result = await this.listDirHandler.handler(args);
53
+
54
+ // 构建文件 URI 列表,用于前端渲染
55
+ const fileUris = result.files.map((file) => {
56
+ const filePath = `${this.listDirHandler.getWorkspaceDir()}/${result.directoryRelativeWorkspacePath}/${file.name}`;
57
+ return {
58
+ uri: filePath,
59
+ isDirectory: file.isDirectory,
60
+ };
61
+ });
62
+
63
+ // 设置消息的附加数据
64
+ const messages = this.chatInternalService.sessionModel.history.getMessages();
65
+ this.chatInternalService.sessionModel.history.setMessageAdditional(messages[messages.length - 1].id, {
66
+ [args.toolCallId]: {
67
+ files: fileUris,
68
+ title: `Listed directory "${args.relativeWorkspacePath}"`,
69
+ details: result.files.map((file) => ({
70
+ type: file.isDirectory ? 'dir' : 'file',
71
+ name: file.name,
72
+ info: file.isDirectory ? `${file.numChildren ?? '?'} items` : `${file.size}KB, ${file.numLines} lines`,
73
+ lastModified: file.lastModified,
74
+ })),
75
+ },
76
+ });
77
+
78
+ logger.appendLine(`Listed ${fileUris.length} files in directory "${args.relativeWorkspacePath}"`);
79
+
46
80
  return {
47
81
  content: [
48
82
  {
49
83
  type: 'text',
50
- text: `Contents of directory:
51
-
52
-
53
- ${result.files
84
+ text: `Contents of directory "${args.relativeWorkspacePath}":\n${result.files
54
85
  .map(
55
86
  (file) =>
56
87
  `[${file.isDirectory ? 'dir' : 'file'}] ${file.name} ${
@@ -32,6 +32,7 @@ export class RunTerminalCommandTool implements MCPServerContribution {
32
32
  getToolDefinition(): MCPToolDefinition {
33
33
  return {
34
34
  name: 'run_terminal_cmd',
35
+ label: 'Run Command',
35
36
  description:
36
37
  "PROPOSE a command to run on behalf of the user.\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\n\nAdhere to these rules:\n1. Based on the contents of the conversation, you will be told if you are in the same shell as a previous step or a new shell.\n2. If in a new shell, you should `cd` to the right directory and do necessary setup in addition to running the command.\n3. If in the same shell, the state will persist, no need to do things like `cd` to the same directory.\n4. For ANY commands that would use a pager, you should append ` | cat` to the command (or whatever is appropriate). You MUST do this for: git, less, head, tail, more, etc.\n5. For commands that are long running/expected to run indefinitely until interruption, please run them in the background. To run jobs in the background, set `is_background` to true rather than changing the details of the command.\n6. Dont include any newlines in the command.",
37
38
  inputSchema,
@@ -128,6 +128,10 @@ export class MsgHistoryManager extends Disposable {
128
128
  return this.messageAdditionalMap.get(id) || {};
129
129
  }
130
130
 
131
+ public get sessionAdditionals() {
132
+ return this.messageAdditionalMap;
133
+ }
134
+
131
135
  toJSON() {
132
136
  return {
133
137
  messages: this.getMessages(),
@@ -69,14 +69,9 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
69
69
  [AINativeSettingSectionsId.LLMModelSelection]: {
70
70
  type: 'string',
71
71
  default: 'deepseek',
72
- enum: ['deepseek', 'anthropic', 'openai', 'openai-compatible'],
72
+ enum: ['deepseek', 'anthropic', 'openai'],
73
73
  description: localize('preference.ai.native.llm.model.selection.description'),
74
74
  },
75
- [AINativeSettingSectionsId.ModelID]: {
76
- type: 'string',
77
- default: 'deepseek-chat',
78
- description: localize('preference.ai.native.llm.model.id'),
79
- },
80
75
  [AINativeSettingSectionsId.DeepseekApiKey]: {
81
76
  type: 'string',
82
77
  default: '',
@@ -153,7 +148,6 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
153
148
  },
154
149
  [AINativeSettingSectionsId.SystemPrompt]: {
155
150
  type: 'string',
156
- default: '',
157
151
  description: localize('preference.ai.native.chat.system.prompt.description'),
158
152
  },
159
153
  },
@@ -1,38 +1,160 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
 
3
- import { Button } from '@opensumi/ide-components';
4
- import { localize, useInjectable } from '@opensumi/ide-core-browser';
5
- import { IResource } from '@opensumi/ide-editor';
3
+ import { Icon, Popover } from '@opensumi/ide-components';
4
+ import { AppConfig, IDisposable, URI, localize, path, useInjectable } from '@opensumi/ide-core-browser';
5
+ import { IResource, WorkbenchEditorService } from '@opensumi/ide-editor';
6
+ import { Path } from '@opensumi/ide-utils/lib/path';
6
7
 
7
8
  import { BaseApplyService } from '../../mcp/base-apply.service';
8
9
 
9
10
  import styles from './inline-diff-widget.module.less';
10
11
 
12
+ const IconWithPopover = (props: {
13
+ icon: string;
14
+ content: string;
15
+ id: string;
16
+ onClick?: () => void;
17
+ disabled?: boolean;
18
+ }) => (
19
+ <Popover content={props.content} id={props.id} onClick={props.onClick}>
20
+ <Icon iconClass={props.icon} className={props.disabled ? styles.disabled : ''} />
21
+ </Popover>
22
+ );
23
+
11
24
  export const InlineDiffManager: React.FC<{ resource: IResource }> = (props) => {
25
+ const { resource } = props;
12
26
  const applyService = useInjectable<BaseApplyService>(BaseApplyService);
27
+ const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
28
+ const appConfig = useInjectable<AppConfig>(AppConfig);
13
29
  const [show, setShow] = useState(true);
30
+ const [changesCount, setChangesCount] = useState(0);
31
+ const [currentChangeIndex, setCurrentChangeIndex] = useState(0);
32
+ const [filePaths, setFilePaths] = useState<string[]>([]);
33
+
34
+ const currentFilePath = useMemo(
35
+ () => path.relative(appConfig.workspaceDir, resource.uri.path.toString()),
36
+ [resource],
37
+ );
38
+
14
39
  useEffect(() => {
15
- applyService.onCodeBlockUpdate((codeBlock) => {
16
- setShow(codeBlock.status === 'pending');
40
+ const toDispose = applyService.onCodeBlockUpdate((codeBlock) => {
41
+ if (path.relative(appConfig.workspaceDir, props.resource.uri.path.toString()) === codeBlock.relativePath) {
42
+ setShow(codeBlock.status === 'pending');
43
+ }
44
+ const pendingPaths = applyService.getPendingPaths();
45
+ setFilePaths(pendingPaths);
17
46
  });
47
+ return () => {
48
+ toDispose.dispose();
49
+ };
18
50
  }, []);
51
+
52
+ // 不同编辑器是不同实例,所以不需要监听
53
+ const decorationModelService = applyService.currentPreviewer?.getNode()?.livePreviewDiffDecorationModel;
54
+
55
+ useEffect(() => {
56
+ let toDispose: IDisposable | undefined;
57
+ if (decorationModelService) {
58
+ setChangesCount(decorationModelService.partialEditWidgetCount);
59
+ toDispose = decorationModelService.onPartialEditWidgetListChange((e) => {
60
+ setChangesCount(e.filter((item) => item.status === 'pending').length);
61
+ });
62
+ }
63
+ return () => {
64
+ toDispose?.dispose();
65
+ };
66
+ }, [decorationModelService]);
67
+
68
+ const handleSiblingChange = useCallback(
69
+ (direction: 'up' | 'down') => {
70
+ const index = decorationModelService?.revealSiblingChange(direction);
71
+ if (index !== undefined) {
72
+ setCurrentChangeIndex(index);
73
+ }
74
+ },
75
+ [decorationModelService],
76
+ );
77
+
78
+ const handleSiblingFile = useCallback(
79
+ (direction: 'up' | 'down') => {
80
+ const index = filePaths.indexOf(currentFilePath!);
81
+ if (index === -1) {
82
+ return;
83
+ }
84
+ const uri = URI.file(path.join(appConfig.workspaceDir, filePaths[index + (direction === 'up' ? -1 : 1)]));
85
+ editorService.open(uri);
86
+ },
87
+ [currentFilePath, filePaths],
88
+ );
89
+
90
+ const changeTip = useMemo(() => {
91
+ if (changesCount === 0) {
92
+ return '';
93
+ }
94
+ return ` ${currentChangeIndex + 1} of ${changesCount}`;
95
+ }, [changesCount, currentChangeIndex]);
96
+
97
+ const fileTip = useMemo(() => {
98
+ if (!currentFilePath) {
99
+ return '';
100
+ }
101
+ return ` ${filePaths.indexOf(currentFilePath) + 1} of ${filePaths.length}`;
102
+ }, [currentFilePath, filePaths]);
103
+
19
104
  return (
20
105
  <div className={styles.inlineDiffManager} style={{ display: show ? 'flex' : 'none' }}>
21
- <Button
22
- onClick={() => {
23
- applyService.processAll(props.resource.uri, 'accept');
24
- }}
25
- >
26
- {localize('aiNative.inlineDiff.acceptAll')}
27
- </Button>
28
- <Button
29
- type='ghost'
30
- onClick={() => {
31
- applyService.processAll(props.resource.uri, 'reject');
32
- }}
33
- >
34
- {localize('aiNative.inlineDiff.rejectAll')}
35
- </Button>
106
+ {/* <div className={styles.left}>
107
+ <IconWithPopover
108
+ icon='codicon codicon-issues'
109
+ content={localize('aiNative.inlineDiff.reveal')}
110
+ id='inline-diff-manager-reveal'
111
+ onClick={handleReveal}
112
+ />
113
+ </div> */}
114
+ <div className={styles.mid}>
115
+ <IconWithPopover
116
+ icon='codicon codicon-check'
117
+ onClick={() => applyService.processAll(props.resource.uri, 'accept')}
118
+ content={localize('aiNative.inlineDiff.acceptAll')}
119
+ id='inline-diff-manager-accept-all'
120
+ />
121
+ <IconWithPopover
122
+ icon='codicon codicon-close'
123
+ onClick={() => applyService.processAll(props.resource.uri, 'reject')}
124
+ content={localize('aiNative.inlineDiff.rejectAll')}
125
+ id='inline-diff-manager-reject-all'
126
+ />
127
+ <IconWithPopover
128
+ icon='codicon codicon-arrow-up'
129
+ content={localize('aiNative.inlineDiff.up') + changeTip}
130
+ id='inline-diff-manager-up'
131
+ disabled={currentChangeIndex === 0}
132
+ onClick={() => handleSiblingChange('up')}
133
+ />
134
+ <IconWithPopover
135
+ icon='codicon codicon-arrow-down'
136
+ content={localize('aiNative.inlineDiff.down') + changeTip}
137
+ id='inline-diff-manager-down'
138
+ disabled={currentChangeIndex === changesCount - 1}
139
+ onClick={() => handleSiblingChange('down')}
140
+ />
141
+ </div>
142
+ <div className={styles.right}>
143
+ <IconWithPopover
144
+ icon='codicon codicon-arrow-left'
145
+ onClick={() => handleSiblingFile('up')}
146
+ disabled={filePaths.length === 0 || filePaths[0] === currentFilePath}
147
+ content={localize('aiNative.inlineDiff.left') + fileTip}
148
+ id='inline-diff-manager-left'
149
+ />
150
+ <IconWithPopover
151
+ icon='codicon codicon-arrow-right'
152
+ onClick={() => handleSiblingFile('down')}
153
+ disabled={filePaths.length === 0 || filePaths[filePaths.length - 1] === currentFilePath}
154
+ content={localize('aiNative.inlineDiff.right') + fileTip}
155
+ id='inline-diff-manager-right'
156
+ />
157
+ </div>
36
158
  </div>
37
159
  );
38
160
  };