@theia/ai-ide 1.59.0-next.62
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/README.md +48 -0
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +27 -0
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/agent-configuration-widget.js +242 -0
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts +13 -0
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-service.js +44 -0
- package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts +12 -0
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +56 -0
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +20 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.js +89 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/language-model-renderer.d.ts +11 -0
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -0
- package/lib/browser/ai-configuration/language-model-renderer.js +125 -0
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -0
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts +12 -0
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -0
- package/lib/browser/ai-configuration/template-settings-renderer.js +57 -0
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -0
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +19 -0
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/variable-configuration-widget.js +98 -0
- package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -0
- package/lib/browser/architect-agent.d.ts +13 -0
- package/lib/browser/architect-agent.d.ts.map +1 -0
- package/lib/browser/architect-agent.js +47 -0
- package/lib/browser/architect-agent.js.map +1 -0
- package/lib/browser/coder-agent.d.ts +13 -0
- package/lib/browser/coder-agent.d.ts.map +1 -0
- package/lib/browser/coder-agent.js +48 -0
- package/lib/browser/coder-agent.js.map +1 -0
- package/lib/browser/content-replacer.d.ts +46 -0
- package/lib/browser/content-replacer.d.ts.map +1 -0
- package/lib/browser/content-replacer.js +115 -0
- package/lib/browser/content-replacer.js.map +1 -0
- package/lib/browser/content-replacer.spec.d.ts +2 -0
- package/lib/browser/content-replacer.spec.d.ts.map +1 -0
- package/lib/browser/content-replacer.spec.js +86 -0
- package/lib/browser/content-replacer.spec.js.map +1 -0
- package/lib/browser/context-functions.d.ts +10 -0
- package/lib/browser/context-functions.d.ts.map +1 -0
- package/lib/browser/context-functions.js +82 -0
- package/lib/browser/context-functions.js.map +1 -0
- package/lib/browser/file-changeset-functions.d.ts +21 -0
- package/lib/browser/file-changeset-functions.d.ts.map +1 -0
- package/lib/browser/file-changeset-functions.js +189 -0
- package/lib/browser/file-changeset-functions.js.map +1 -0
- package/lib/browser/frontend-module.d.ts +4 -0
- package/lib/browser/frontend-module.d.ts.map +1 -0
- package/lib/browser/frontend-module.js +87 -0
- package/lib/browser/frontend-module.js.map +1 -0
- package/lib/browser/workspace-functions.d.ts +48 -0
- package/lib/browser/workspace-functions.d.ts.map +1 -0
- package/lib/browser/workspace-functions.js +306 -0
- package/lib/browser/workspace-functions.js.map +1 -0
- package/lib/browser/workspace-preferences.d.ts +5 -0
- package/lib/browser/workspace-preferences.d.ts.map +1 -0
- package/lib/browser/workspace-preferences.js +42 -0
- package/lib/browser/workspace-preferences.js.map +1 -0
- package/lib/common/architect-prompt-template.d.ts +3 -0
- package/lib/common/architect-prompt-template.d.ts.map +1 -0
- package/lib/common/architect-prompt-template.js +29 -0
- package/lib/common/architect-prompt-template.js.map +1 -0
- package/lib/common/coder-replace-prompt-template.d.ts +5 -0
- package/lib/common/coder-replace-prompt-template.d.ts.map +1 -0
- package/lib/common/coder-replace-prompt-template.js +46 -0
- package/lib/common/coder-replace-prompt-template.js.map +1 -0
- package/lib/common/command-chat-agents.d.ts +37 -0
- package/lib/common/command-chat-agents.d.ts.map +1 -0
- package/lib/common/command-chat-agents.js +329 -0
- package/lib/common/command-chat-agents.js.map +1 -0
- package/lib/common/orchestrator-chat-agent.d.ts +24 -0
- package/lib/common/orchestrator-chat-agent.d.ts.map +1 -0
- package/lib/common/orchestrator-chat-agent.js +164 -0
- package/lib/common/orchestrator-chat-agent.js.map +1 -0
- package/lib/common/universal-chat-agent.d.ts +15 -0
- package/lib/common/universal-chat-agent.d.ts.map +1 -0
- package/lib/common/universal-chat-agent.js +107 -0
- package/lib/common/universal-chat-agent.js.map +1 -0
- package/lib/common/workspace-functions.d.ts +4 -0
- package/lib/common/workspace-functions.d.ts.map +1 -0
- package/lib/common/workspace-functions.js +22 -0
- package/lib/common/workspace-functions.js.map +1 -0
- package/lib/package.spec.d.ts +1 -0
- package/lib/package.spec.d.ts.map +1 -0
- package/lib/package.spec.js +26 -0
- package/lib/package.spec.js.map +1 -0
- package/package.json +57 -0
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +330 -0
- package/src/browser/ai-configuration/ai-configuration-service.ts +43 -0
- package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +53 -0
- package/src/browser/ai-configuration/ai-configuration-widget.tsx +81 -0
- package/src/browser/ai-configuration/language-model-renderer.tsx +122 -0
- package/src/browser/ai-configuration/template-settings-renderer.tsx +128 -0
- package/src/browser/ai-configuration/variable-configuration-widget.tsx +108 -0
- package/src/browser/architect-agent.ts +41 -0
- package/src/browser/coder-agent.ts +42 -0
- package/src/browser/content-replacer.spec.ts +92 -0
- package/src/browser/content-replacer.ts +126 -0
- package/src/browser/context-functions.ts +78 -0
- package/src/browser/file-changeset-functions.ts +185 -0
- package/src/browser/frontend-module.ts +102 -0
- package/src/browser/style/index.css +127 -0
- package/src/browser/workspace-functions.ts +326 -0
- package/src/browser/workspace-preferences.ts +43 -0
- package/src/common/architect-prompt-template.ts +42 -0
- package/src/common/coder-replace-prompt-template.ts +49 -0
- package/src/common/command-chat-agents.ts +343 -0
- package/src/common/orchestrator-chat-agent.ts +169 -0
- package/src/common/universal-chat-agent.ts +102 -0
- package/src/common/workspace-functions.ts +18 -0
- package/src/package.spec.ts +28 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
.ai-configuration-widget {
|
|
2
|
+
padding: var(--theia-ui-padding);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.theia-ai-settings-container {
|
|
6
|
+
padding: var(--theia-ui-padding);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.language-model-container {
|
|
10
|
+
padding-top: calc(2 * var(--theia-ui-padding));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.language-model-container .theia-select {
|
|
14
|
+
margin-left: var(--theia-ui-padding);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.theia-settings-container .settings-section-subcategory-title.ai-settings-section-subcategory-title {
|
|
18
|
+
padding-left: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ai-templates {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 5px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.template-renderer {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
padding: 10px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.template-header {
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.template-controls {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.template-select-label {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.template-variant-selector {
|
|
48
|
+
min-width: 120px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.template-variant-selector.error {
|
|
52
|
+
border-color: var(--theia-errorForeground);
|
|
53
|
+
background-color: var(--theia-errorBackground, rgba(255, 0, 0, 0.1));
|
|
54
|
+
color: var(--theia-errorForeground);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#ai-variable-configuration-container-widget,
|
|
58
|
+
#ai-agent-configuration-container-widget {
|
|
59
|
+
margin-top: 5px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Variable Settings */
|
|
63
|
+
#ai-variable-configuration-container-widget ul {
|
|
64
|
+
list-style: none;
|
|
65
|
+
padding: 0;
|
|
66
|
+
margin: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#ai-variable-configuration-container-widget .variable-item {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
margin-bottom: 1rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#ai-variable-configuration-container-widget .variable-args {
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-columns: 1fr 2fr;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Agent Settings */
|
|
81
|
+
#ai-agent-configuration-container-widget ul {
|
|
82
|
+
list-style: none;
|
|
83
|
+
padding: 0;
|
|
84
|
+
margin: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.ai-agent-configuration-main {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: row;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.configuration-agents-list {
|
|
93
|
+
width: 128px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.configuration-agent-panel {
|
|
97
|
+
flex: 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#ai-variable-configuration-container-widget .variable-references,
|
|
101
|
+
#ai-agent-configuration-container-widget .variable-references,
|
|
102
|
+
#ai-agent-configuration-container-widget .function-references {
|
|
103
|
+
margin-left: 0.5rem;
|
|
104
|
+
padding: 0.5rem;
|
|
105
|
+
border-left: solid 1px var(--theia-tree-indentGuidesStroke);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#ai-variable-configuration-container-widget .variable-reference,
|
|
109
|
+
#ai-agent-configuration-container-widget .variable-reference,
|
|
110
|
+
#ai-agent-configuration-container-widget .function-reference {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: row;
|
|
113
|
+
align-items: center;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.agent-tag {
|
|
117
|
+
padding: calc(var(--theia-ui-padding) * 2 / 3);
|
|
118
|
+
padding-top: 0px;
|
|
119
|
+
padding-bottom: 0px;
|
|
120
|
+
border-radius: calc(var(--theia-ui-padding) * 2 / 3);
|
|
121
|
+
background: hsla(0, 0%, 68%, 0.31);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.configuration-agents-add {
|
|
125
|
+
margin-top: 3em;
|
|
126
|
+
margin-left: 0;
|
|
127
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
import { ToolProvider, ToolRequest } from '@theia/ai-core';
|
|
17
|
+
import { URI } from '@theia/core';
|
|
18
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
19
|
+
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
20
|
+
import { FileStat } from '@theia/filesystem/lib/common/files';
|
|
21
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
22
|
+
import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID } from '../common/workspace-functions';
|
|
23
|
+
import ignore from 'ignore';
|
|
24
|
+
import { Minimatch } from 'minimatch';
|
|
25
|
+
import { PreferenceService } from '@theia/core/lib/browser';
|
|
26
|
+
import { CONSIDER_GITIGNORE_PREF, USER_EXCLUDE_PATTERN_PREF } from './workspace-preferences';
|
|
27
|
+
import { MonacoWorkspace } from '@theia/monaco/lib/browser/monaco-workspace';
|
|
28
|
+
|
|
29
|
+
@injectable()
|
|
30
|
+
export class WorkspaceFunctionScope {
|
|
31
|
+
protected readonly GITIGNORE_FILE_NAME = '.gitignore';
|
|
32
|
+
|
|
33
|
+
@inject(WorkspaceService)
|
|
34
|
+
protected workspaceService: WorkspaceService;
|
|
35
|
+
|
|
36
|
+
@inject(FileService)
|
|
37
|
+
protected fileService: FileService;
|
|
38
|
+
|
|
39
|
+
@inject(PreferenceService)
|
|
40
|
+
protected preferences: PreferenceService;
|
|
41
|
+
|
|
42
|
+
private gitignoreMatcher: ReturnType<typeof ignore> | undefined;
|
|
43
|
+
private gitignoreWatcherInitialized = false;
|
|
44
|
+
|
|
45
|
+
async getWorkspaceRoot(): Promise<URI> {
|
|
46
|
+
const wsRoots = await this.workspaceService.roots;
|
|
47
|
+
if (wsRoots.length === 0) {
|
|
48
|
+
throw new Error('No workspace has been opened yet');
|
|
49
|
+
}
|
|
50
|
+
return wsRoots[0].resource;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ensureWithinWorkspace(targetUri: URI, workspaceRootUri: URI): void {
|
|
54
|
+
if (!targetUri.toString().startsWith(workspaceRootUri.toString())) {
|
|
55
|
+
throw new Error('Access outside of the workspace is not allowed');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async resolveRelativePath(relativePath: string): Promise<URI> {
|
|
60
|
+
const workspaceRoot = await this.getWorkspaceRoot();
|
|
61
|
+
return workspaceRoot.resolve(relativePath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async initializeGitignoreWatcher(workspaceRoot: URI): Promise<void> {
|
|
65
|
+
if (this.gitignoreWatcherInitialized) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const gitignoreUri = workspaceRoot.resolve(this.GITIGNORE_FILE_NAME);
|
|
70
|
+
this.fileService.watch(gitignoreUri);
|
|
71
|
+
|
|
72
|
+
this.fileService.onDidFilesChange(async event => {
|
|
73
|
+
if (event.contains(gitignoreUri)) {
|
|
74
|
+
this.gitignoreMatcher = undefined;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.gitignoreWatcherInitialized = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async shouldExclude(stat: FileStat): Promise<boolean> {
|
|
82
|
+
const shouldConsiderGitIgnore = this.preferences.get(CONSIDER_GITIGNORE_PREF, false);
|
|
83
|
+
const userExcludePatterns = this.preferences.get<string[]>(USER_EXCLUDE_PATTERN_PREF, []);
|
|
84
|
+
|
|
85
|
+
if (this.isUserExcluded(stat.resource.path.base, userExcludePatterns)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
const workspaceRoot = await this.getWorkspaceRoot();
|
|
89
|
+
if (shouldConsiderGitIgnore && await this.isGitIgnored(stat, workspaceRoot)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected isUserExcluded(fileName: string, userExcludePatterns: string[]): boolean {
|
|
97
|
+
return userExcludePatterns.some(pattern => new Minimatch(pattern, { dot: true }).match(fileName));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
protected async isGitIgnored(stat: FileStat, workspaceRoot: URI): Promise<boolean> {
|
|
101
|
+
await this.initializeGitignoreWatcher(workspaceRoot);
|
|
102
|
+
|
|
103
|
+
const gitignoreUri = workspaceRoot.resolve(this.GITIGNORE_FILE_NAME);
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const fileStat = await this.fileService.resolve(gitignoreUri);
|
|
107
|
+
if (fileStat) {
|
|
108
|
+
if (!this.gitignoreMatcher) {
|
|
109
|
+
const gitignoreContent = await this.fileService.read(gitignoreUri);
|
|
110
|
+
this.gitignoreMatcher = ignore().add(gitignoreContent.value);
|
|
111
|
+
}
|
|
112
|
+
const relativePath = workspaceRoot.relative(stat.resource);
|
|
113
|
+
if (relativePath) {
|
|
114
|
+
const relativePathStr = relativePath.toString() + (stat.isDirectory ? '/' : '');
|
|
115
|
+
if (this.gitignoreMatcher.ignores(relativePathStr)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
// If .gitignore does not exist or cannot be read, continue without error
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@injectable()
|
|
130
|
+
export class GetWorkspaceDirectoryStructure implements ToolProvider {
|
|
131
|
+
static ID = GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID;
|
|
132
|
+
|
|
133
|
+
getTool(): ToolRequest {
|
|
134
|
+
return {
|
|
135
|
+
id: GetWorkspaceDirectoryStructure.ID,
|
|
136
|
+
name: GetWorkspaceDirectoryStructure.ID,
|
|
137
|
+
description: `Retrieve the complete directory structure of the workspace, listing only directories (no file contents). This structure excludes specific directories,
|
|
138
|
+
such as node_modules and hidden files, ensuring paths are within workspace boundaries.`,
|
|
139
|
+
handler: () => this.getDirectoryStructure()
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@inject(FileService)
|
|
144
|
+
protected readonly fileService: FileService;
|
|
145
|
+
|
|
146
|
+
@inject(WorkspaceFunctionScope)
|
|
147
|
+
protected workspaceScope: WorkspaceFunctionScope;
|
|
148
|
+
|
|
149
|
+
private async getDirectoryStructure(): Promise<string[]> {
|
|
150
|
+
let workspaceRoot;
|
|
151
|
+
try {
|
|
152
|
+
workspaceRoot = await this.workspaceScope.getWorkspaceRoot();
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return [`Error: ${error.message}`];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.buildDirectoryStructure(workspaceRoot);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async buildDirectoryStructure(uri: URI, prefix: string = ''): Promise<string[]> {
|
|
161
|
+
const stat = await this.fileService.resolve(uri);
|
|
162
|
+
const result: string[] = [];
|
|
163
|
+
|
|
164
|
+
if (stat && stat.isDirectory && stat.children) {
|
|
165
|
+
for (const child of stat.children) {
|
|
166
|
+
if (!child.isDirectory || await this.workspaceScope.shouldExclude(child)) { continue; };
|
|
167
|
+
const path = `${prefix}${child.resource.path.base}/`;
|
|
168
|
+
result.push(path);
|
|
169
|
+
result.push(...await this.buildDirectoryStructure(child.resource, `${path}`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@injectable()
|
|
178
|
+
export class FileContentFunction implements ToolProvider {
|
|
179
|
+
static ID = FILE_CONTENT_FUNCTION_ID;
|
|
180
|
+
|
|
181
|
+
getTool(): ToolRequest {
|
|
182
|
+
return {
|
|
183
|
+
id: FileContentFunction.ID,
|
|
184
|
+
name: FileContentFunction.ID,
|
|
185
|
+
description: `Return the content of a specified file within the workspace. The file path must be provided relative to the workspace root. Only files within
|
|
186
|
+
workspace boundaries are accessible; attempting to access files outside the workspace will return an error.`,
|
|
187
|
+
parameters: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
properties: {
|
|
190
|
+
file: {
|
|
191
|
+
type: 'string',
|
|
192
|
+
description: `The relative path to the target file within the workspace. This path is resolved from the workspace root, and only files within the workspace
|
|
193
|
+
boundaries are accessible. Attempting to access paths outside the workspace will result in an error.`,
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
required: ['file']
|
|
197
|
+
},
|
|
198
|
+
handler: (arg_string: string) => {
|
|
199
|
+
const file = this.parseArg(arg_string);
|
|
200
|
+
return this.getFileContent(file);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@inject(FileService)
|
|
206
|
+
protected readonly fileService: FileService;
|
|
207
|
+
|
|
208
|
+
@inject(WorkspaceFunctionScope)
|
|
209
|
+
protected readonly workspaceScope: WorkspaceFunctionScope;
|
|
210
|
+
|
|
211
|
+
@inject(MonacoWorkspace)
|
|
212
|
+
protected readonly monacoWorkspace: MonacoWorkspace;
|
|
213
|
+
|
|
214
|
+
private parseArg(arg_string: string): string {
|
|
215
|
+
const result = JSON.parse(arg_string);
|
|
216
|
+
return result.file;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private async getFileContent(file: string): Promise<string> {
|
|
220
|
+
let workspaceRoot;
|
|
221
|
+
try {
|
|
222
|
+
workspaceRoot = await this.workspaceScope.getWorkspaceRoot();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return JSON.stringify({ error: error.message });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const targetUri = workspaceRoot.resolve(file);
|
|
228
|
+
this.workspaceScope.ensureWithinWorkspace(targetUri, workspaceRoot);
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const openEditorValue = this.monacoWorkspace.getTextDocument(targetUri.toString())?.getText();
|
|
232
|
+
if (openEditorValue !== undefined) {
|
|
233
|
+
return openEditorValue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const fileContent = await this.fileService.read(targetUri);
|
|
237
|
+
return fileContent.value;
|
|
238
|
+
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return JSON.stringify({ error: 'File not found' });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@injectable()
|
|
246
|
+
export class GetWorkspaceFileList implements ToolProvider {
|
|
247
|
+
static ID = GET_WORKSPACE_FILE_LIST_FUNCTION_ID;
|
|
248
|
+
|
|
249
|
+
getTool(): ToolRequest {
|
|
250
|
+
return {
|
|
251
|
+
id: GetWorkspaceFileList.ID,
|
|
252
|
+
name: GetWorkspaceFileList.ID,
|
|
253
|
+
parameters: {
|
|
254
|
+
type: 'object',
|
|
255
|
+
properties: {
|
|
256
|
+
path: {
|
|
257
|
+
type: 'string',
|
|
258
|
+
description: `Optional relative path to a directory within the workspace. If no path is specified, the function lists contents directly in the workspace
|
|
259
|
+
root. Paths are resolved within workspace boundaries only; paths outside the workspace or unvalidated paths will result in an error.`
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
required: ['path']
|
|
263
|
+
},
|
|
264
|
+
description: `List files and directories within a specified workspace directory. Paths are relative to the workspace root, and only workspace-contained paths are
|
|
265
|
+
allowed. If no path is provided, the root contents are listed. Paths outside the workspace will result in an error.`,
|
|
266
|
+
handler: (arg_string: string) => {
|
|
267
|
+
const args = JSON.parse(arg_string);
|
|
268
|
+
return this.getProjectFileList(args.path);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@inject(FileService)
|
|
274
|
+
protected readonly fileService: FileService;
|
|
275
|
+
|
|
276
|
+
@inject(WorkspaceFunctionScope)
|
|
277
|
+
protected workspaceScope: WorkspaceFunctionScope;
|
|
278
|
+
|
|
279
|
+
async getProjectFileList(path?: string): Promise<string[]> {
|
|
280
|
+
let workspaceRoot;
|
|
281
|
+
try {
|
|
282
|
+
workspaceRoot = await this.workspaceScope.getWorkspaceRoot();
|
|
283
|
+
} catch (error) {
|
|
284
|
+
return [`Error: ${error.message}`];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const targetUri = path ? workspaceRoot.resolve(path) : workspaceRoot;
|
|
288
|
+
this.workspaceScope.ensureWithinWorkspace(targetUri, workspaceRoot);
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const stat = await this.fileService.resolve(targetUri);
|
|
292
|
+
if (!stat || !stat.isDirectory) {
|
|
293
|
+
return ['Error: Directory not found'];
|
|
294
|
+
}
|
|
295
|
+
return await this.listFilesDirectly(targetUri, workspaceRoot);
|
|
296
|
+
|
|
297
|
+
} catch (error) {
|
|
298
|
+
return ['Error: Directory not found'];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private async listFilesDirectly(uri: URI, workspaceRootUri: URI): Promise<string[]> {
|
|
303
|
+
const stat = await this.fileService.resolve(uri);
|
|
304
|
+
const result: string[] = [];
|
|
305
|
+
|
|
306
|
+
if (stat && stat.isDirectory) {
|
|
307
|
+
if (await this.workspaceScope.shouldExclude(stat)) {
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
const children = await this.fileService.resolve(uri);
|
|
311
|
+
if (children.children) {
|
|
312
|
+
for (const child of children.children) {
|
|
313
|
+
if (await this.workspaceScope.shouldExclude(child)) {
|
|
314
|
+
continue;
|
|
315
|
+
};
|
|
316
|
+
const relativePath = workspaceRootUri.relative(child.resource);
|
|
317
|
+
if (relativePath) {
|
|
318
|
+
result.push(relativePath.toString());
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { nls } from '@theia/core';
|
|
18
|
+
import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';
|
|
19
|
+
|
|
20
|
+
export const CONSIDER_GITIGNORE_PREF = 'ai-features.workspaceFunctions.considerGitIgnore';
|
|
21
|
+
export const USER_EXCLUDE_PATTERN_PREF = 'ai-features.workspaceFunctions.userExcludes';
|
|
22
|
+
|
|
23
|
+
export const WorkspacePreferencesSchema: PreferenceSchema = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
[CONSIDER_GITIGNORE_PREF]: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
title: nls.localize('theia/ai/workspace/considerGitignore/title', 'Consider .gitignore'),
|
|
29
|
+
description: nls.localize('theia/ai/workspace/considerGitignore/description',
|
|
30
|
+
'If enabled, excludes files/folders specified in a global .gitignore file (expected location is the workspace root).'),
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
[USER_EXCLUDE_PATTERN_PREF]: {
|
|
34
|
+
type: 'array',
|
|
35
|
+
title: nls.localize('theia/ai/workspace/excludedPattern/title', 'Excluded File Patterns'),
|
|
36
|
+
description: nls.localize('theia/ai/workspace/excludedPattern/description', 'List of patterns (glob or regex) for files/folders to exclude.'),
|
|
37
|
+
default: ['node_modules', 'lib', '.*'],
|
|
38
|
+
items: {
|
|
39
|
+
type: 'string'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
import { PromptTemplate } from '@theia/ai-core/lib/common';
|
|
17
|
+
import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID } from './workspace-functions';
|
|
18
|
+
|
|
19
|
+
export const architectPromptTemplate = <PromptTemplate>{
|
|
20
|
+
id: 'architect-system',
|
|
21
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
22
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
23
|
+
# Instructions
|
|
24
|
+
|
|
25
|
+
You are an AI assistant integrated into Theia IDE, designed to assist software developers. You can't change any files, but you can navigate and read the users workspace using \
|
|
26
|
+
the provided functions. Therefore describe and explain the details or procedures necessary to achieve the desired outcome. If file changes are necessary to help the user, be \
|
|
27
|
+
aware that there is another agent called 'Coder' that can suggest file changes. In this case you can create a description on what to do and tell the user to ask '@Coder' to \
|
|
28
|
+
implement the change plan. If you refer to files, always mention the workspace-relative path.\
|
|
29
|
+
|
|
30
|
+
Use the following functions to interact with the workspace files as needed:
|
|
31
|
+
- **~{${GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID}}**: Returns the complete directory structure.
|
|
32
|
+
- **~{${GET_WORKSPACE_FILE_LIST_FUNCTION_ID}}**: Lists files and directories in a specific directory.
|
|
33
|
+
- **~{${FILE_CONTENT_FUNCTION_ID}}**: Retrieves the content of a specific file.
|
|
34
|
+
|
|
35
|
+
### Workspace Navigation Guidelines
|
|
36
|
+
|
|
37
|
+
1. **Start at the Root**: For general questions (e.g., "How to build the project"), check root-level documentation files or setup files before browsing subdirectories.
|
|
38
|
+
2. **Confirm Paths**: Always verify paths by listing directories or files as you navigate. Avoid assumptions based on user input alone.
|
|
39
|
+
3. **Navigate Step-by-Step**: Move into subdirectories only as needed, confirming each directory level.
|
|
40
|
+
|
|
41
|
+
`
|
|
42
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2024 EclipseSource GmbH.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
*
|
|
9
|
+
* This Source Code may also be made available under the following Secondary
|
|
10
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
* with the GNU Classpath Exception which is available at
|
|
13
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
*/
|
|
17
|
+
// *****************************************************************************
|
|
18
|
+
|
|
19
|
+
import { PromptTemplate } from '@theia/ai-core/lib/common';
|
|
20
|
+
import {
|
|
21
|
+
GET_WORKSPACE_FILE_LIST_FUNCTION_ID,
|
|
22
|
+
FILE_CONTENT_FUNCTION_ID,
|
|
23
|
+
GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID
|
|
24
|
+
} from './workspace-functions';
|
|
25
|
+
|
|
26
|
+
export const CODER_REWRITE_PROMPT_TEMPLATE_ID = 'coder-rewrite';
|
|
27
|
+
export const CODER_REPLACE_PROMPT_TEMPLATE_ID = 'coder-search-replace';
|
|
28
|
+
|
|
29
|
+
export function getCoderReplacePromptTemplate(withSearchAndReplace: boolean = false): PromptTemplate {
|
|
30
|
+
return {
|
|
31
|
+
id: withSearchAndReplace ? CODER_REPLACE_PROMPT_TEMPLATE_ID : CODER_REWRITE_PROMPT_TEMPLATE_ID,
|
|
32
|
+
template: `You are an AI assistant integrated into Theia IDE, designed to assist software developers with code tasks. You can interact with the code base and suggest changes.
|
|
33
|
+
|
|
34
|
+
## Context Retrieval
|
|
35
|
+
Use the following functions to interact with the workspace files if you require context:
|
|
36
|
+
- **~{${GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID}}**: Returns the complete directory structure.
|
|
37
|
+
- **~{${GET_WORKSPACE_FILE_LIST_FUNCTION_ID}}**: Lists files and directories in a specific directory.
|
|
38
|
+
- **~{${FILE_CONTENT_FUNCTION_ID}}**: Retrieves the content of a specific file.
|
|
39
|
+
|
|
40
|
+
## Propose Code Changes
|
|
41
|
+
To propose code changes or any file changes to the user, never print code or new file content in your response.
|
|
42
|
+
|
|
43
|
+
Instead, for each file you want to propose changes for:
|
|
44
|
+
- **Always Retrieve Current Content**: Use ${FILE_CONTENT_FUNCTION_ID} to get the latest content of the target file.
|
|
45
|
+
- **Change Content**: Use ~{changeSet_writeChangeToFile}${withSearchAndReplace ? ' or ~{changeSet_replaceContentInFile}' : ''} to suggest file changes to the user.\
|
|
46
|
+
${withSearchAndReplace ? 'Only select and call one function per file.' : ''}`,
|
|
47
|
+
...(!withSearchAndReplace ? { variantOf: CODER_REPLACE_PROMPT_TEMPLATE_ID } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|