@theia/ai-terminal 1.46.0-next.241
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 +31 -0
- package/lib/browser/ai-terminal-agent.d.ts +27 -0
- package/lib/browser/ai-terminal-agent.d.ts.map +1 -0
- package/lib/browser/ai-terminal-agent.js +212 -0
- package/lib/browser/ai-terminal-agent.js.map +1 -0
- package/lib/browser/ai-terminal-contribution.d.ts +15 -0
- package/lib/browser/ai-terminal-contribution.d.ts.map +1 -0
- package/lib/browser/ai-terminal-contribution.js +176 -0
- package/lib/browser/ai-terminal-contribution.js.map +1 -0
- package/lib/browser/ai-terminal-frontend-module.d.ts +5 -0
- package/lib/browser/ai-terminal-frontend-module.d.ts.map +1 -0
- package/lib/browser/ai-terminal-frontend-module.js +33 -0
- package/lib/browser/ai-terminal-frontend-module.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 +52 -0
- package/src/browser/ai-terminal-agent.ts +223 -0
- package/src/browser/ai-terminal-contribution.ts +197 -0
- package/src/browser/ai-terminal-frontend-module.ts +34 -0
- package/src/browser/style/ai-terminal.css +94 -0
- package/src/package.spec.ts +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<div align='center'>
|
|
2
|
+
|
|
3
|
+
<br />
|
|
4
|
+
|
|
5
|
+
<img src='https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/theia.svg?sanitize=true' alt='theia-ext-logo' width='100px' />
|
|
6
|
+
|
|
7
|
+
<h2>ECLIPSE THEIA - AI Terminal EXTENSION</h2>
|
|
8
|
+
|
|
9
|
+
<hr />
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
## Description
|
|
14
|
+
|
|
15
|
+
The `@theia/ai-terminal` extension contributes an overlay to the terminal view.\
|
|
16
|
+
The overlay can be used to ask a dedicated `TerminalAgent` for suggestions of terminal commands.
|
|
17
|
+
|
|
18
|
+
## Additional Information
|
|
19
|
+
|
|
20
|
+
- [Theia - GitHub](https://github.com/eclipse-theia/theia)
|
|
21
|
+
- [Theia - Website](https://theia-ide.org/)
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
|
|
26
|
+
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
|
|
27
|
+
|
|
28
|
+
## Trademark
|
|
29
|
+
|
|
30
|
+
"Theia" is a trademark of the Eclipse Foundation
|
|
31
|
+
<https://www.eclipse.org/theia>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Agent, CommunicationRecordingService, LanguageModelRegistry, LanguageModelRequirement, PromptService } from '@theia/ai-core/lib/common';
|
|
2
|
+
import { ILogger } from '@theia/core';
|
|
3
|
+
export declare class AiTerminalAgent implements Agent {
|
|
4
|
+
protected recordingService: CommunicationRecordingService;
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
variables: never[];
|
|
9
|
+
functions: never[];
|
|
10
|
+
agentSpecificVariables: {
|
|
11
|
+
name: string;
|
|
12
|
+
usedInPrompt: boolean;
|
|
13
|
+
description: string;
|
|
14
|
+
}[];
|
|
15
|
+
promptTemplates: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
template: string;
|
|
20
|
+
}[];
|
|
21
|
+
languageModelRequirements: LanguageModelRequirement[];
|
|
22
|
+
protected languageModelRegistry: LanguageModelRegistry;
|
|
23
|
+
protected promptService: PromptService;
|
|
24
|
+
protected logger: ILogger;
|
|
25
|
+
getCommands(userRequest: string, cwd: string, shell: string, recentTerminalContents: string[]): Promise<string[]>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ai-terminal-agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-agent.d.ts","sourceRoot":"","sources":["../../src/browser/ai-terminal-agent.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,KAAK,EACL,6BAA6B,EAG7B,qBAAqB,EAAE,wBAAwB,EAC/C,aAAa,EAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAgB,OAAO,EAAE,MAAM,aAAa,CAAC;AAUpD,qBACa,eAAgB,YAAW,KAAK;IAEzC,SAAS,CAAC,gBAAgB,EAAE,6BAA6B,CAAC;IAE1D,EAAE,SAAwB;IAC1B,IAAI,SAAwB;IAC5B,WAAW,SAEoI;IAC/I,SAAS,UAAM;IACf,SAAS,UAAM;IACf,sBAAsB;;;;QAKpB;IACF,eAAe;;;;;QA8Db;IACF,yBAAyB,EAAE,wBAAwB,EAAE,CAKnD;IAGF,SAAS,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IAGvD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAEpB,WAAW,CACb,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,sBAAsB,EAAE,MAAM,EAAE,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC;CAsFvB"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.AiTerminalAgent = void 0;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const common_1 = require("@theia/ai-core/lib/common");
|
|
21
|
+
const core_1 = require("@theia/core");
|
|
22
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
23
|
+
const zod_1 = require("zod");
|
|
24
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
25
|
+
const Commands = zod_1.z.object({
|
|
26
|
+
commands: zod_1.z.array(zod_1.z.string()),
|
|
27
|
+
});
|
|
28
|
+
let AiTerminalAgent = class AiTerminalAgent {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.id = 'Terminal Assistant';
|
|
31
|
+
this.name = 'Terminal Assistant';
|
|
32
|
+
this.description = 'This agent provides assistance to write and execute arbitrary terminal commands. \
|
|
33
|
+
Based on the user\'s request, it suggests commands and allows the user to directly paste and execute them in the terminal. \
|
|
34
|
+
It accesses the current directory, environment and the recent terminal output of the terminal session to provide context-aware assistance';
|
|
35
|
+
this.variables = [];
|
|
36
|
+
this.functions = [];
|
|
37
|
+
this.agentSpecificVariables = [
|
|
38
|
+
{ name: 'userRequest', usedInPrompt: true, description: 'The user\'s question or request.' },
|
|
39
|
+
{ name: 'shell', usedInPrompt: true, description: 'The shell being used, e.g., /usr/bin/zsh.' },
|
|
40
|
+
{ name: 'cwd', usedInPrompt: true, description: 'The current working directory.' },
|
|
41
|
+
{ name: 'recentTerminalContents', usedInPrompt: true, description: 'The last 0 to 50 recent lines visible in the terminal.' }
|
|
42
|
+
];
|
|
43
|
+
this.promptTemplates = [
|
|
44
|
+
{
|
|
45
|
+
id: 'terminal-system',
|
|
46
|
+
name: 'AI Terminal System Prompt',
|
|
47
|
+
description: 'Prompt for the AI Terminal Assistant',
|
|
48
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
49
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
50
|
+
# Instructions
|
|
51
|
+
Generate one or more command suggestions based on the user's request, considering the shell being used,
|
|
52
|
+
the current working directory, and the recent terminal contents. Provide the best suggestion first,
|
|
53
|
+
followed by other relevant suggestions if the user asks for further options.
|
|
54
|
+
|
|
55
|
+
Parameters:
|
|
56
|
+
- user-request: The user's question or request.
|
|
57
|
+
- shell: The shell being used, e.g., /usr/bin/zsh.
|
|
58
|
+
- cwd: The current working directory.
|
|
59
|
+
- recent-terminal-contents: The last 0 to 50 recent lines visible in the terminal.
|
|
60
|
+
|
|
61
|
+
Return the result in the following JSON format:
|
|
62
|
+
{
|
|
63
|
+
"commands": [
|
|
64
|
+
"best_command_suggestion",
|
|
65
|
+
"next_best_command_suggestion",
|
|
66
|
+
"another_command_suggestion"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
## Example
|
|
71
|
+
user-request: "How do I commit changes?"
|
|
72
|
+
shell: "/usr/bin/zsh"
|
|
73
|
+
cwd: "/home/user/project"
|
|
74
|
+
recent-terminal-contents:
|
|
75
|
+
git status
|
|
76
|
+
On branch main
|
|
77
|
+
Your branch is up to date with 'origin/main'.
|
|
78
|
+
nothing to commit, working tree clean
|
|
79
|
+
|
|
80
|
+
## Expected JSON output
|
|
81
|
+
\`\`\`json
|
|
82
|
+
\{
|
|
83
|
+
"commands": [
|
|
84
|
+
"git commit",
|
|
85
|
+
"git commit --amend",
|
|
86
|
+
"git commit -a"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
\`\`\`
|
|
90
|
+
`
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'terminal-user',
|
|
94
|
+
name: 'AI Terminal User Prompt',
|
|
95
|
+
description: 'Prompt that contains the user request',
|
|
96
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
97
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
98
|
+
user-request: {{userRequest}}
|
|
99
|
+
shell: {{shell}}
|
|
100
|
+
cwd: {{cwd}}
|
|
101
|
+
recent-terminal-contents:
|
|
102
|
+
{{recentTerminalContents}}
|
|
103
|
+
`
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
this.languageModelRequirements = [
|
|
107
|
+
{
|
|
108
|
+
purpose: 'suggest-terminal-commands',
|
|
109
|
+
identifier: 'openai/gpt-4o',
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
async getCommands(userRequest, cwd, shell, recentTerminalContents) {
|
|
114
|
+
const lm = await this.languageModelRegistry.selectLanguageModel({
|
|
115
|
+
agent: this.id,
|
|
116
|
+
...this.languageModelRequirements[0]
|
|
117
|
+
});
|
|
118
|
+
if (!lm) {
|
|
119
|
+
this.logger.error('No language model available for the AI Terminal Agent.');
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const parameters = {
|
|
123
|
+
userRequest,
|
|
124
|
+
shell,
|
|
125
|
+
cwd,
|
|
126
|
+
recentTerminalContents
|
|
127
|
+
};
|
|
128
|
+
const systemMessage = await this.promptService.getPrompt('terminal-system', parameters).then(p => p === null || p === void 0 ? void 0 : p.text);
|
|
129
|
+
const request = await this.promptService.getPrompt('terminal-user', parameters).then(p => p === null || p === void 0 ? void 0 : p.text);
|
|
130
|
+
if (!systemMessage || !request) {
|
|
131
|
+
this.logger.error('The prompt service didn\'t return prompts for the AI Terminal Agent.');
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
// since we do not actually hold complete conversions, the request/response pair is considered a session
|
|
135
|
+
const sessionId = (0, core_1.generateUuid)();
|
|
136
|
+
const requestId = (0, core_1.generateUuid)();
|
|
137
|
+
this.recordingService.recordRequest({
|
|
138
|
+
agentId: this.id,
|
|
139
|
+
sessionId,
|
|
140
|
+
requestId,
|
|
141
|
+
request,
|
|
142
|
+
systemMessage
|
|
143
|
+
});
|
|
144
|
+
try {
|
|
145
|
+
const result = await lm.request({
|
|
146
|
+
messages: [
|
|
147
|
+
{
|
|
148
|
+
actor: 'ai',
|
|
149
|
+
type: 'text',
|
|
150
|
+
query: systemMessage
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
actor: 'user',
|
|
154
|
+
type: 'text',
|
|
155
|
+
query: request
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
response_format: {
|
|
159
|
+
type: 'json_schema',
|
|
160
|
+
json_schema: {
|
|
161
|
+
name: 'terminal-commands',
|
|
162
|
+
description: 'Suggested terminal commands based on the user request',
|
|
163
|
+
schema: (0, zod_to_json_schema_1.default)(Commands)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if ((0, common_1.isLanguageModelParsedResponse)(result)) {
|
|
168
|
+
// model returned structured output
|
|
169
|
+
const parsedResult = Commands.safeParse(result.parsed);
|
|
170
|
+
if (parsedResult.success) {
|
|
171
|
+
const response = JSON.stringify(parsedResult.data.commands);
|
|
172
|
+
this.recordingService.recordResponse({ agentId: this.id, sessionId, requestId, response, systemMessage });
|
|
173
|
+
return parsedResult.data.commands;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// fall back to agent-based parsing of result
|
|
177
|
+
const jsonResult = await (0, common_1.getJsonOfResponse)(result);
|
|
178
|
+
const responseTextFromJSON = JSON.stringify(jsonResult);
|
|
179
|
+
this.recordingService.recordResponse({ agentId: this.id, sessionId, requestId, response: responseTextFromJSON });
|
|
180
|
+
const parsedJsonResult = Commands.safeParse(jsonResult);
|
|
181
|
+
if (parsedJsonResult.success) {
|
|
182
|
+
return parsedJsonResult.data.commands;
|
|
183
|
+
}
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
this.logger.error('Error obtaining the command suggestions.', error);
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
exports.AiTerminalAgent = AiTerminalAgent;
|
|
193
|
+
tslib_1.__decorate([
|
|
194
|
+
(0, inversify_1.inject)(common_1.CommunicationRecordingService),
|
|
195
|
+
tslib_1.__metadata("design:type", Object)
|
|
196
|
+
], AiTerminalAgent.prototype, "recordingService", void 0);
|
|
197
|
+
tslib_1.__decorate([
|
|
198
|
+
(0, inversify_1.inject)(common_1.LanguageModelRegistry),
|
|
199
|
+
tslib_1.__metadata("design:type", Object)
|
|
200
|
+
], AiTerminalAgent.prototype, "languageModelRegistry", void 0);
|
|
201
|
+
tslib_1.__decorate([
|
|
202
|
+
(0, inversify_1.inject)(common_1.PromptService),
|
|
203
|
+
tslib_1.__metadata("design:type", Object)
|
|
204
|
+
], AiTerminalAgent.prototype, "promptService", void 0);
|
|
205
|
+
tslib_1.__decorate([
|
|
206
|
+
(0, inversify_1.inject)(core_1.ILogger),
|
|
207
|
+
tslib_1.__metadata("design:type", Object)
|
|
208
|
+
], AiTerminalAgent.prototype, "logger", void 0);
|
|
209
|
+
exports.AiTerminalAgent = AiTerminalAgent = tslib_1.__decorate([
|
|
210
|
+
(0, inversify_1.injectable)()
|
|
211
|
+
], AiTerminalAgent);
|
|
212
|
+
//# sourceMappingURL=ai-terminal-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-agent.js","sourceRoot":"","sources":["../../src/browser/ai-terminal-agent.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;AAEhF,sDAOmC;AACnC,sCAAoD;AACpD,4DAAkE;AAClE,6BAAwB;AACxB,2DAAiD;AAEjD,MAAM,QAAQ,GAAG,OAAC,CAAC,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC;AAII,IAAM,eAAe,GAArB,MAAM,eAAe;IAArB;QAIH,OAAE,GAAG,oBAAoB,CAAC;QAC1B,SAAI,GAAG,oBAAoB,CAAC;QAC5B,gBAAW,GAAG;;kJAEgI,CAAC;QAC/I,cAAS,GAAG,EAAE,CAAC;QACf,cAAS,GAAG,EAAE,CAAC;QACf,2BAAsB,GAAG;YACrB,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE;YAC5F,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,2CAA2C,EAAE;YAC/F,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE;YAClF,EAAE,IAAI,EAAE,wBAAwB,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE;SAChI,CAAC;QACF,oBAAe,GAAG;YACd;gBACI,EAAE,EAAE,iBAAiB;gBACrB,IAAI,EAAE,2BAA2B;gBACjC,WAAW,EAAE,sCAAsC;gBACnD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CrB;aACQ;YACD;gBACI,EAAE,EAAE,eAAe;gBACnB,IAAI,EAAE,yBAAyB;gBAC/B,WAAW,EAAE,uCAAuC;gBACpD,QAAQ,EAAE;;;;;;;CAOrB;aACQ;SACJ,CAAC;QACF,8BAAyB,GAA+B;YACpD;gBACI,OAAO,EAAE,2BAA2B;gBACpC,UAAU,EAAE,eAAe;aAC9B;SACJ,CAAC;IAsGN,CAAC;IA3FG,KAAK,CAAC,WAAW,CACb,WAAmB,EACnB,GAAW,EACX,KAAa,EACb,sBAAgC;QAEhC,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,mBAAmB,CAAC;YAC5D,KAAK,EAAE,IAAI,CAAC,EAAE;YACd,GAAG,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;SACvC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,EAAE,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC5E,OAAO,EAAE,CAAC;QACd,CAAC;QAED,MAAM,UAAU,GAAG;YACf,WAAW;YACX,KAAK;YACL,GAAG;YACH,sBAAsB;SACzB,CAAC;QAEF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI,CAAC,CAAC;QAC3G,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI,CAAC,CAAC;QACnG,IAAI,CAAC,aAAa,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAC;YAC1F,OAAO,EAAE,CAAC;QACd,CAAC;QAED,wGAAwG;QACxG,MAAM,SAAS,GAAG,IAAA,mBAAY,GAAE,CAAC;QACjC,MAAM,SAAS,GAAG,IAAA,mBAAY,GAAE,CAAC;QACjC,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC;YAChC,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,SAAS;YACT,SAAS;YACT,OAAO;YACP,aAAa;SAChB,CAAC,CAAC;QAEH,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC;gBAC5B,QAAQ,EAAE;oBACN;wBACI,KAAK,EAAE,IAAI;wBACX,IAAI,EAAE,MAAM;wBACZ,KAAK,EAAE,aAAa;qBACvB;oBACD;wBACI,KAAK,EAAE,MAAM;wBACb,IAAI,EAAE,MAAM;wBACZ,KAAK,EAAE,OAAO;qBACjB;iBACJ;gBACD,eAAe,EAAE;oBACb,IAAI,EAAE,aAAa;oBACnB,WAAW,EAAE;wBACT,IAAI,EAAE,mBAAmB;wBACzB,WAAW,EAAE,uDAAuD;wBACpE,MAAM,EAAE,IAAA,4BAAe,EAAC,QAAQ,CAAC;qBACpC;iBACJ;aACJ,CAAC,CAAC;YAEH,IAAI,IAAA,sCAA6B,EAAC,MAAM,CAAC,EAAE,CAAC;gBACxC,mCAAmC;gBACnC,MAAM,YAAY,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACvD,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;oBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC5D,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC;oBAC1G,OAAO,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACtC,CAAC;YACL,CAAC;YAED,6CAA6C;YAC7C,MAAM,UAAU,GAAG,MAAM,IAAA,0BAAiB,EAAC,MAAM,CAAC,CAAC;YACnD,MAAM,oBAAoB,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACxD,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC,CAAC;YACjH,MAAM,gBAAgB,GAAG,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACxD,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;gBAC3B,OAAO,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC1C,CAAC;YAED,OAAO,EAAE,CAAC;QAEd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;YACrE,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;CAEJ,CAAA;AA3LY,0CAAe;AAEd;IADT,IAAA,kBAAM,EAAC,sCAA6B,CAAC;;yDACoB;AAsFhD;IADT,IAAA,kBAAM,EAAC,8BAAqB,CAAC;;8DACyB;AAG7C;IADT,IAAA,kBAAM,EAAC,sBAAa,CAAC;;sDACiB;AAG7B;IADT,IAAA,kBAAM,EAAC,cAAO,CAAC;;+CACU;0BA9FjB,eAAe;IAD3B,IAAA,sBAAU,GAAE;GACA,eAAe,CA2L3B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core';
|
|
2
|
+
import { KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser';
|
|
3
|
+
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
|
4
|
+
import { AiTerminalAgent } from './ai-terminal-agent';
|
|
5
|
+
import { AICommandHandlerFactory } from '@theia/ai-core/lib/browser/ai-command-handler-factory';
|
|
6
|
+
export declare class AiTerminalCommandContribution implements CommandContribution, MenuContribution, KeybindingContribution {
|
|
7
|
+
protected terminalService: TerminalService;
|
|
8
|
+
protected terminalAgent: AiTerminalAgent;
|
|
9
|
+
protected commandHandlerFactory: AICommandHandlerFactory;
|
|
10
|
+
private readonly agentService;
|
|
11
|
+
registerKeybindings(keybindings: KeybindingRegistry): void;
|
|
12
|
+
registerMenus(menus: MenuModelRegistry): void;
|
|
13
|
+
registerCommands(commands: CommandRegistry): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=ai-terminal-contribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-contribution.d.ts","sourceRoot":"","sources":["../../src/browser/ai-terminal-contribution.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAGpF,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uDAAuD,CAAC;AAQhG,qBACa,6BAA8B,YAAW,mBAAmB,EAAE,gBAAgB,EAAE,sBAAsB;IAG/G,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,aAAa,EAAE,eAAe,CAAC;IAGzC,SAAS,CAAC,qBAAqB,EAAE,uBAAuB,CAAC;IAGzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAE5C,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAO1D,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAM7C,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;CAYpD"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.AiTerminalCommandContribution = void 0;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const browser_1 = require("@theia/ai-core/lib/browser");
|
|
21
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
22
|
+
const terminal_service_1 = require("@theia/terminal/lib/browser/base/terminal-service");
|
|
23
|
+
const terminal_frontend_contribution_1 = require("@theia/terminal/lib/browser/terminal-frontend-contribution");
|
|
24
|
+
const terminal_widget_impl_1 = require("@theia/terminal/lib/browser/terminal-widget-impl");
|
|
25
|
+
const ai_terminal_agent_1 = require("./ai-terminal-agent");
|
|
26
|
+
const ai_command_handler_factory_1 = require("@theia/ai-core/lib/browser/ai-command-handler-factory");
|
|
27
|
+
const ai_core_1 = require("@theia/ai-core");
|
|
28
|
+
const AI_TERMINAL_COMMAND = {
|
|
29
|
+
id: 'ai-terminal:open',
|
|
30
|
+
label: 'Ask the AI'
|
|
31
|
+
};
|
|
32
|
+
let AiTerminalCommandContribution = class AiTerminalCommandContribution {
|
|
33
|
+
registerKeybindings(keybindings) {
|
|
34
|
+
keybindings.registerKeybinding({
|
|
35
|
+
command: AI_TERMINAL_COMMAND.id,
|
|
36
|
+
keybinding: 'ctrlcmd+i',
|
|
37
|
+
when: `terminalFocus && ${browser_1.EXPERIMENTAL_AI_CONTEXT_KEY}`
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
registerMenus(menus) {
|
|
41
|
+
menus.registerMenuAction([...terminal_frontend_contribution_1.TerminalMenus.TERMINAL_CONTEXT_MENU, '_5'], {
|
|
42
|
+
when: browser_1.EXPERIMENTAL_AI_CONTEXT_KEY,
|
|
43
|
+
commandId: AI_TERMINAL_COMMAND.id
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
registerCommands(commands) {
|
|
47
|
+
commands.registerCommand(AI_TERMINAL_COMMAND, this.commandHandlerFactory({
|
|
48
|
+
execute: () => {
|
|
49
|
+
if (this.terminalService.currentTerminal instanceof terminal_widget_impl_1.TerminalWidgetImpl && this.agentService.isEnabled(this.terminalAgent.id)) {
|
|
50
|
+
new AiTerminalChatWidget(this.terminalService.currentTerminal, this.terminalAgent);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.AiTerminalCommandContribution = AiTerminalCommandContribution;
|
|
57
|
+
tslib_1.__decorate([
|
|
58
|
+
(0, inversify_1.inject)(terminal_service_1.TerminalService),
|
|
59
|
+
tslib_1.__metadata("design:type", Object)
|
|
60
|
+
], AiTerminalCommandContribution.prototype, "terminalService", void 0);
|
|
61
|
+
tslib_1.__decorate([
|
|
62
|
+
(0, inversify_1.inject)(ai_terminal_agent_1.AiTerminalAgent),
|
|
63
|
+
tslib_1.__metadata("design:type", ai_terminal_agent_1.AiTerminalAgent)
|
|
64
|
+
], AiTerminalCommandContribution.prototype, "terminalAgent", void 0);
|
|
65
|
+
tslib_1.__decorate([
|
|
66
|
+
(0, inversify_1.inject)(ai_command_handler_factory_1.AICommandHandlerFactory),
|
|
67
|
+
tslib_1.__metadata("design:type", Function)
|
|
68
|
+
], AiTerminalCommandContribution.prototype, "commandHandlerFactory", void 0);
|
|
69
|
+
tslib_1.__decorate([
|
|
70
|
+
(0, inversify_1.inject)(ai_core_1.AgentService),
|
|
71
|
+
tslib_1.__metadata("design:type", Object)
|
|
72
|
+
], AiTerminalCommandContribution.prototype, "agentService", void 0);
|
|
73
|
+
exports.AiTerminalCommandContribution = AiTerminalCommandContribution = tslib_1.__decorate([
|
|
74
|
+
(0, inversify_1.injectable)()
|
|
75
|
+
], AiTerminalCommandContribution);
|
|
76
|
+
class AiTerminalChatWidget {
|
|
77
|
+
constructor(terminalWidget, terminalAgent) {
|
|
78
|
+
this.terminalWidget = terminalWidget;
|
|
79
|
+
this.terminalAgent = terminalAgent;
|
|
80
|
+
this.haveResult = false;
|
|
81
|
+
this.chatContainer = document.createElement('div');
|
|
82
|
+
this.chatContainer.className = 'ai-terminal-chat-container';
|
|
83
|
+
const chatCloseButton = document.createElement('span');
|
|
84
|
+
chatCloseButton.className = 'closeButton codicon codicon-close';
|
|
85
|
+
chatCloseButton.onclick = () => this.dispose();
|
|
86
|
+
this.chatContainer.appendChild(chatCloseButton);
|
|
87
|
+
const chatResultContainer = document.createElement('div');
|
|
88
|
+
chatResultContainer.className = 'ai-terminal-chat-result';
|
|
89
|
+
this.chatResultParagraph = document.createElement('p');
|
|
90
|
+
this.chatResultParagraph.textContent = 'How can I help you?';
|
|
91
|
+
chatResultContainer.appendChild(this.chatResultParagraph);
|
|
92
|
+
this.chatContainer.appendChild(chatResultContainer);
|
|
93
|
+
this.chatInputContainer = document.createElement('div');
|
|
94
|
+
this.chatInputContainer.className = 'ai-terminal-chat-input-container';
|
|
95
|
+
this.chatInput = document.createElement('textarea');
|
|
96
|
+
this.chatInput.className = 'theia-input theia-ChatInput';
|
|
97
|
+
this.chatInput.placeholder = 'Ask about a terminal command...';
|
|
98
|
+
this.chatInput.onkeydown = event => {
|
|
99
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
if (!this.haveResult) {
|
|
102
|
+
this.send();
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.terminalWidget.sendText(this.chatResultParagraph.innerText);
|
|
106
|
+
this.dispose();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (event.key === 'Escape') {
|
|
110
|
+
this.dispose();
|
|
111
|
+
}
|
|
112
|
+
else if (event.key === 'ArrowUp' && this.haveResult) {
|
|
113
|
+
this.updateChatResult(this.getNextCommandIndex(1));
|
|
114
|
+
}
|
|
115
|
+
else if (event.key === 'ArrowDown' && this.haveResult) {
|
|
116
|
+
this.updateChatResult(this.getNextCommandIndex(-1));
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
this.chatInputContainer.appendChild(this.chatInput);
|
|
120
|
+
const chatInputOptionsContainer = document.createElement('div');
|
|
121
|
+
const chatInputOptionsSpan = document.createElement('span');
|
|
122
|
+
chatInputOptionsSpan.className = 'codicon codicon-send option';
|
|
123
|
+
chatInputOptionsSpan.title = 'Send';
|
|
124
|
+
chatInputOptionsSpan.onclick = () => this.send();
|
|
125
|
+
chatInputOptionsContainer.appendChild(chatInputOptionsSpan);
|
|
126
|
+
this.chatInputContainer.appendChild(chatInputOptionsContainer);
|
|
127
|
+
this.chatContainer.appendChild(this.chatInputContainer);
|
|
128
|
+
terminalWidget.node.appendChild(this.chatContainer);
|
|
129
|
+
this.chatInput.focus();
|
|
130
|
+
}
|
|
131
|
+
async send() {
|
|
132
|
+
const userRequest = this.chatInput.value;
|
|
133
|
+
if (userRequest) {
|
|
134
|
+
this.chatInput.value = '';
|
|
135
|
+
this.chatResultParagraph.innerText = 'Loading';
|
|
136
|
+
this.chatResultParagraph.className = 'loading';
|
|
137
|
+
const cwd = (await this.terminalWidget.cwd).toString();
|
|
138
|
+
const processInfo = await this.terminalWidget.processInfo;
|
|
139
|
+
const shell = processInfo.executable;
|
|
140
|
+
const recentTerminalContents = this.getRecentTerminalCommands();
|
|
141
|
+
this.commands = await this.terminalAgent.getCommands(userRequest, cwd, shell, recentTerminalContents);
|
|
142
|
+
if (this.commands.length > 0) {
|
|
143
|
+
this.chatResultParagraph.className = 'command';
|
|
144
|
+
this.chatResultParagraph.innerText = this.commands[0];
|
|
145
|
+
this.chatInput.placeholder = 'Hit enter to confirm';
|
|
146
|
+
if (this.commands.length > 1) {
|
|
147
|
+
this.chatInput.placeholder += ' or use ⇅ to show alternatives...';
|
|
148
|
+
}
|
|
149
|
+
this.haveResult = true;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.chatResultParagraph.className = '';
|
|
153
|
+
this.chatResultParagraph.innerText = 'No results';
|
|
154
|
+
this.chatInput.placeholder = 'Try again...';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
getRecentTerminalCommands() {
|
|
159
|
+
const maxLines = 100;
|
|
160
|
+
return this.terminalWidget.buffer.getLines(0, this.terminalWidget.buffer.length > maxLines ? maxLines : this.terminalWidget.buffer.length);
|
|
161
|
+
}
|
|
162
|
+
getNextCommandIndex(step) {
|
|
163
|
+
const currentIndex = this.commands.indexOf(this.chatResultParagraph.innerText);
|
|
164
|
+
const nextIndex = (currentIndex + step + this.commands.length) % this.commands.length;
|
|
165
|
+
return nextIndex;
|
|
166
|
+
}
|
|
167
|
+
updateChatResult(index) {
|
|
168
|
+
this.chatResultParagraph.innerText = this.commands[index];
|
|
169
|
+
}
|
|
170
|
+
dispose() {
|
|
171
|
+
this.chatInput.value = '';
|
|
172
|
+
this.terminalWidget.node.removeChild(this.chatContainer);
|
|
173
|
+
this.terminalWidget.getTerminal().focus();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=ai-terminal-contribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-contribution.js","sourceRoot":"","sources":["../../src/browser/ai-terminal-contribution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-frontend-module.d.ts","sourceRoot":"","sources":["../../src/browser/ai-terminal-frontend-module.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAI/D,OAAO,yCAAyC,CAAC;;AAEjD,wBAQG"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const common_1 = require("@theia/ai-core/lib/common");
|
|
19
|
+
const core_1 = require("@theia/core");
|
|
20
|
+
const browser_1 = require("@theia/core/lib/browser");
|
|
21
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
22
|
+
const ai_terminal_agent_1 = require("./ai-terminal-agent");
|
|
23
|
+
const ai_terminal_contribution_1 = require("./ai-terminal-contribution");
|
|
24
|
+
require("../../src/browser/style/ai-terminal.css");
|
|
25
|
+
exports.default = new inversify_1.ContainerModule(bind => {
|
|
26
|
+
bind(ai_terminal_contribution_1.AiTerminalCommandContribution).toSelf().inSingletonScope();
|
|
27
|
+
for (const identifier of [core_1.CommandContribution, core_1.MenuContribution, browser_1.KeybindingContribution]) {
|
|
28
|
+
bind(identifier).toService(ai_terminal_contribution_1.AiTerminalCommandContribution);
|
|
29
|
+
}
|
|
30
|
+
bind(ai_terminal_agent_1.AiTerminalAgent).toSelf().inSingletonScope();
|
|
31
|
+
bind(common_1.Agent).toService(ai_terminal_agent_1.AiTerminalAgent);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=ai-terminal-frontend-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-terminal-frontend-module.js","sourceRoot":"","sources":["../../src/browser/ai-terminal-frontend-module.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;AAEhF,sDAAkD;AAClD,sCAAoE;AACpE,qDAAiE;AACjE,4DAA+D;AAC/D,2DAAsD;AACtD,yEAA2E;AAE3E,mDAAiD;AAEjD,kBAAe,IAAI,2BAAe,CAAC,IAAI,CAAC,EAAE;IACtC,IAAI,CAAC,wDAA6B,CAAC,CAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,CAAC;IAChE,KAAK,MAAM,UAAU,IAAI,CAAC,0BAAmB,EAAE,uBAAgB,EAAE,gCAAsB,CAAC,EAAE,CAAC;QACvF,IAAI,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,wDAA6B,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,CAAC,mCAAe,CAAC,CAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,CAAC;IAClD,IAAI,CAAC,cAAK,CAAC,CAAC,SAAS,CAAC,mCAAe,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=package.spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.spec.d.ts","sourceRoot":"","sources":["../src/package.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH and others.
|
|
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
|
+
/* note: this bogus test file is required so that
|
|
17
|
+
we are able to run mocha unit tests on this
|
|
18
|
+
package, without having any actual unit tests in it.
|
|
19
|
+
This way a coverage report will be generated,
|
|
20
|
+
showing 0% coverage, instead of no report.
|
|
21
|
+
This file can be removed once we have real unit
|
|
22
|
+
tests in place. */
|
|
23
|
+
describe('ai-terminal package', () => {
|
|
24
|
+
it('support code coverage statistics', () => true);
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=package.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.spec.js","sourceRoot":"","sources":["../src/package.spec.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oDAAoD;AACpD,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAEhF;;;;;;qBAMqB;AAErB,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IAEjC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theia/ai-terminal",
|
|
3
|
+
"version": "1.46.0-next.241+4295c1a8c",
|
|
4
|
+
"description": "Theia - AI Terminal Extension",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@theia/ai-chat": "1.46.0-next.241+4295c1a8c",
|
|
7
|
+
"@theia/ai-core": "1.46.0-next.241+4295c1a8c",
|
|
8
|
+
"@theia/core": "1.46.0-next.241+4295c1a8c",
|
|
9
|
+
"@theia/terminal": "1.46.0-next.241+4295c1a8c",
|
|
10
|
+
"zod": "^3.23.8",
|
|
11
|
+
"zod-to-json-schema": "^3.23.2"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"theiaExtensions": [
|
|
17
|
+
{
|
|
18
|
+
"frontend": "lib/browser/ai-terminal-frontend-module"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"theia-extension"
|
|
23
|
+
],
|
|
24
|
+
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/eclipse-theia/theia.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/eclipse-theia/theia/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/eclipse-theia/theia",
|
|
33
|
+
"files": [
|
|
34
|
+
"lib",
|
|
35
|
+
"src"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "theiaext build",
|
|
39
|
+
"clean": "theiaext clean",
|
|
40
|
+
"compile": "theiaext compile",
|
|
41
|
+
"lint": "theiaext lint",
|
|
42
|
+
"test": "theiaext test",
|
|
43
|
+
"watch": "theiaext watch"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@theia/ext-scripts": "1.56.0"
|
|
47
|
+
},
|
|
48
|
+
"nyc": {
|
|
49
|
+
"extends": "../../configs/nyc.json"
|
|
50
|
+
},
|
|
51
|
+
"gitHead": "4295c1a8c8b5d23471f274265c798bac9e6baecc"
|
|
52
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
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 {
|
|
18
|
+
Agent,
|
|
19
|
+
CommunicationRecordingService,
|
|
20
|
+
getJsonOfResponse,
|
|
21
|
+
isLanguageModelParsedResponse,
|
|
22
|
+
LanguageModelRegistry, LanguageModelRequirement,
|
|
23
|
+
PromptService
|
|
24
|
+
} from '@theia/ai-core/lib/common';
|
|
25
|
+
import { generateUuid, ILogger } from '@theia/core';
|
|
26
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
import zodToJsonSchema from 'zod-to-json-schema';
|
|
29
|
+
|
|
30
|
+
const Commands = z.object({
|
|
31
|
+
commands: z.array(z.string()),
|
|
32
|
+
});
|
|
33
|
+
type Commands = z.infer<typeof Commands>;
|
|
34
|
+
|
|
35
|
+
@injectable()
|
|
36
|
+
export class AiTerminalAgent implements Agent {
|
|
37
|
+
@inject(CommunicationRecordingService)
|
|
38
|
+
protected recordingService: CommunicationRecordingService;
|
|
39
|
+
|
|
40
|
+
id = 'Terminal Assistant';
|
|
41
|
+
name = 'Terminal Assistant';
|
|
42
|
+
description = 'This agent provides assistance to write and execute arbitrary terminal commands. \
|
|
43
|
+
Based on the user\'s request, it suggests commands and allows the user to directly paste and execute them in the terminal. \
|
|
44
|
+
It accesses the current directory, environment and the recent terminal output of the terminal session to provide context-aware assistance';
|
|
45
|
+
variables = [];
|
|
46
|
+
functions = [];
|
|
47
|
+
agentSpecificVariables = [
|
|
48
|
+
{ name: 'userRequest', usedInPrompt: true, description: 'The user\'s question or request.' },
|
|
49
|
+
{ name: 'shell', usedInPrompt: true, description: 'The shell being used, e.g., /usr/bin/zsh.' },
|
|
50
|
+
{ name: 'cwd', usedInPrompt: true, description: 'The current working directory.' },
|
|
51
|
+
{ name: 'recentTerminalContents', usedInPrompt: true, description: 'The last 0 to 50 recent lines visible in the terminal.' }
|
|
52
|
+
];
|
|
53
|
+
promptTemplates = [
|
|
54
|
+
{
|
|
55
|
+
id: 'terminal-system',
|
|
56
|
+
name: 'AI Terminal System Prompt',
|
|
57
|
+
description: 'Prompt for the AI Terminal Assistant',
|
|
58
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
59
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
60
|
+
# Instructions
|
|
61
|
+
Generate one or more command suggestions based on the user's request, considering the shell being used,
|
|
62
|
+
the current working directory, and the recent terminal contents. Provide the best suggestion first,
|
|
63
|
+
followed by other relevant suggestions if the user asks for further options.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
- user-request: The user's question or request.
|
|
67
|
+
- shell: The shell being used, e.g., /usr/bin/zsh.
|
|
68
|
+
- cwd: The current working directory.
|
|
69
|
+
- recent-terminal-contents: The last 0 to 50 recent lines visible in the terminal.
|
|
70
|
+
|
|
71
|
+
Return the result in the following JSON format:
|
|
72
|
+
{
|
|
73
|
+
"commands": [
|
|
74
|
+
"best_command_suggestion",
|
|
75
|
+
"next_best_command_suggestion",
|
|
76
|
+
"another_command_suggestion"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
## Example
|
|
81
|
+
user-request: "How do I commit changes?"
|
|
82
|
+
shell: "/usr/bin/zsh"
|
|
83
|
+
cwd: "/home/user/project"
|
|
84
|
+
recent-terminal-contents:
|
|
85
|
+
git status
|
|
86
|
+
On branch main
|
|
87
|
+
Your branch is up to date with 'origin/main'.
|
|
88
|
+
nothing to commit, working tree clean
|
|
89
|
+
|
|
90
|
+
## Expected JSON output
|
|
91
|
+
\`\`\`json
|
|
92
|
+
\{
|
|
93
|
+
"commands": [
|
|
94
|
+
"git commit",
|
|
95
|
+
"git commit --amend",
|
|
96
|
+
"git commit -a"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
\`\`\`
|
|
100
|
+
`
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'terminal-user',
|
|
104
|
+
name: 'AI Terminal User Prompt',
|
|
105
|
+
description: 'Prompt that contains the user request',
|
|
106
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
107
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
108
|
+
user-request: {{userRequest}}
|
|
109
|
+
shell: {{shell}}
|
|
110
|
+
cwd: {{cwd}}
|
|
111
|
+
recent-terminal-contents:
|
|
112
|
+
{{recentTerminalContents}}
|
|
113
|
+
`
|
|
114
|
+
}
|
|
115
|
+
];
|
|
116
|
+
languageModelRequirements: LanguageModelRequirement[] = [
|
|
117
|
+
{
|
|
118
|
+
purpose: 'suggest-terminal-commands',
|
|
119
|
+
identifier: 'openai/gpt-4o',
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
@inject(LanguageModelRegistry)
|
|
124
|
+
protected languageModelRegistry: LanguageModelRegistry;
|
|
125
|
+
|
|
126
|
+
@inject(PromptService)
|
|
127
|
+
protected promptService: PromptService;
|
|
128
|
+
|
|
129
|
+
@inject(ILogger)
|
|
130
|
+
protected logger: ILogger;
|
|
131
|
+
|
|
132
|
+
async getCommands(
|
|
133
|
+
userRequest: string,
|
|
134
|
+
cwd: string,
|
|
135
|
+
shell: string,
|
|
136
|
+
recentTerminalContents: string[],
|
|
137
|
+
): Promise<string[]> {
|
|
138
|
+
const lm = await this.languageModelRegistry.selectLanguageModel({
|
|
139
|
+
agent: this.id,
|
|
140
|
+
...this.languageModelRequirements[0]
|
|
141
|
+
});
|
|
142
|
+
if (!lm) {
|
|
143
|
+
this.logger.error('No language model available for the AI Terminal Agent.');
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parameters = {
|
|
148
|
+
userRequest,
|
|
149
|
+
shell,
|
|
150
|
+
cwd,
|
|
151
|
+
recentTerminalContents
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const systemMessage = await this.promptService.getPrompt('terminal-system', parameters).then(p => p?.text);
|
|
155
|
+
const request = await this.promptService.getPrompt('terminal-user', parameters).then(p => p?.text);
|
|
156
|
+
if (!systemMessage || !request) {
|
|
157
|
+
this.logger.error('The prompt service didn\'t return prompts for the AI Terminal Agent.');
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// since we do not actually hold complete conversions, the request/response pair is considered a session
|
|
162
|
+
const sessionId = generateUuid();
|
|
163
|
+
const requestId = generateUuid();
|
|
164
|
+
this.recordingService.recordRequest({
|
|
165
|
+
agentId: this.id,
|
|
166
|
+
sessionId,
|
|
167
|
+
requestId,
|
|
168
|
+
request,
|
|
169
|
+
systemMessage
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const result = await lm.request({
|
|
174
|
+
messages: [
|
|
175
|
+
{
|
|
176
|
+
actor: 'ai',
|
|
177
|
+
type: 'text',
|
|
178
|
+
query: systemMessage
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
actor: 'user',
|
|
182
|
+
type: 'text',
|
|
183
|
+
query: request
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
response_format: {
|
|
187
|
+
type: 'json_schema',
|
|
188
|
+
json_schema: {
|
|
189
|
+
name: 'terminal-commands',
|
|
190
|
+
description: 'Suggested terminal commands based on the user request',
|
|
191
|
+
schema: zodToJsonSchema(Commands)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (isLanguageModelParsedResponse(result)) {
|
|
197
|
+
// model returned structured output
|
|
198
|
+
const parsedResult = Commands.safeParse(result.parsed);
|
|
199
|
+
if (parsedResult.success) {
|
|
200
|
+
const response = JSON.stringify(parsedResult.data.commands);
|
|
201
|
+
this.recordingService.recordResponse({ agentId: this.id, sessionId, requestId, response, systemMessage });
|
|
202
|
+
return parsedResult.data.commands;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// fall back to agent-based parsing of result
|
|
207
|
+
const jsonResult = await getJsonOfResponse(result);
|
|
208
|
+
const responseTextFromJSON = JSON.stringify(jsonResult);
|
|
209
|
+
this.recordingService.recordResponse({ agentId: this.id, sessionId, requestId, response: responseTextFromJSON });
|
|
210
|
+
const parsedJsonResult = Commands.safeParse(jsonResult);
|
|
211
|
+
if (parsedJsonResult.success) {
|
|
212
|
+
return parsedJsonResult.data.commands;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return [];
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
this.logger.error('Error obtaining the command suggestions.', error);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
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 { EXPERIMENTAL_AI_CONTEXT_KEY } from '@theia/ai-core/lib/browser';
|
|
18
|
+
import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core';
|
|
19
|
+
import { KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser';
|
|
20
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
21
|
+
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
|
22
|
+
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
|
23
|
+
import { TerminalWidgetImpl } from '@theia/terminal/lib/browser/terminal-widget-impl';
|
|
24
|
+
import { AiTerminalAgent } from './ai-terminal-agent';
|
|
25
|
+
import { AICommandHandlerFactory } from '@theia/ai-core/lib/browser/ai-command-handler-factory';
|
|
26
|
+
import { AgentService } from '@theia/ai-core';
|
|
27
|
+
|
|
28
|
+
const AI_TERMINAL_COMMAND = {
|
|
29
|
+
id: 'ai-terminal:open',
|
|
30
|
+
label: 'Ask the AI'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
@injectable()
|
|
34
|
+
export class AiTerminalCommandContribution implements CommandContribution, MenuContribution, KeybindingContribution {
|
|
35
|
+
|
|
36
|
+
@inject(TerminalService)
|
|
37
|
+
protected terminalService: TerminalService;
|
|
38
|
+
|
|
39
|
+
@inject(AiTerminalAgent)
|
|
40
|
+
protected terminalAgent: AiTerminalAgent;
|
|
41
|
+
|
|
42
|
+
@inject(AICommandHandlerFactory)
|
|
43
|
+
protected commandHandlerFactory: AICommandHandlerFactory;
|
|
44
|
+
|
|
45
|
+
@inject(AgentService)
|
|
46
|
+
private readonly agentService: AgentService;
|
|
47
|
+
|
|
48
|
+
registerKeybindings(keybindings: KeybindingRegistry): void {
|
|
49
|
+
keybindings.registerKeybinding({
|
|
50
|
+
command: AI_TERMINAL_COMMAND.id,
|
|
51
|
+
keybinding: 'ctrlcmd+i',
|
|
52
|
+
when: `terminalFocus && ${EXPERIMENTAL_AI_CONTEXT_KEY}`
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
registerMenus(menus: MenuModelRegistry): void {
|
|
56
|
+
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_5'], {
|
|
57
|
+
when: EXPERIMENTAL_AI_CONTEXT_KEY,
|
|
58
|
+
commandId: AI_TERMINAL_COMMAND.id
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
registerCommands(commands: CommandRegistry): void {
|
|
62
|
+
commands.registerCommand(AI_TERMINAL_COMMAND, this.commandHandlerFactory({
|
|
63
|
+
execute: () => {
|
|
64
|
+
if (this.terminalService.currentTerminal instanceof TerminalWidgetImpl && this.agentService.isEnabled(this.terminalAgent.id)) {
|
|
65
|
+
new AiTerminalChatWidget(
|
|
66
|
+
this.terminalService.currentTerminal,
|
|
67
|
+
this.terminalAgent
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class AiTerminalChatWidget {
|
|
76
|
+
|
|
77
|
+
protected chatContainer: HTMLDivElement;
|
|
78
|
+
protected chatInput: HTMLTextAreaElement;
|
|
79
|
+
protected chatResultParagraph: HTMLParagraphElement;
|
|
80
|
+
protected chatInputContainer: HTMLDivElement;
|
|
81
|
+
|
|
82
|
+
protected haveResult = false;
|
|
83
|
+
commands: string[];
|
|
84
|
+
|
|
85
|
+
constructor(
|
|
86
|
+
protected terminalWidget: TerminalWidgetImpl,
|
|
87
|
+
protected terminalAgent: AiTerminalAgent
|
|
88
|
+
) {
|
|
89
|
+
this.chatContainer = document.createElement('div');
|
|
90
|
+
this.chatContainer.className = 'ai-terminal-chat-container';
|
|
91
|
+
|
|
92
|
+
const chatCloseButton = document.createElement('span');
|
|
93
|
+
chatCloseButton.className = 'closeButton codicon codicon-close';
|
|
94
|
+
chatCloseButton.onclick = () => this.dispose();
|
|
95
|
+
this.chatContainer.appendChild(chatCloseButton);
|
|
96
|
+
|
|
97
|
+
const chatResultContainer = document.createElement('div');
|
|
98
|
+
chatResultContainer.className = 'ai-terminal-chat-result';
|
|
99
|
+
this.chatResultParagraph = document.createElement('p');
|
|
100
|
+
this.chatResultParagraph.textContent = 'How can I help you?';
|
|
101
|
+
chatResultContainer.appendChild(this.chatResultParagraph);
|
|
102
|
+
this.chatContainer.appendChild(chatResultContainer);
|
|
103
|
+
|
|
104
|
+
this.chatInputContainer = document.createElement('div');
|
|
105
|
+
this.chatInputContainer.className = 'ai-terminal-chat-input-container';
|
|
106
|
+
|
|
107
|
+
this.chatInput = document.createElement('textarea');
|
|
108
|
+
this.chatInput.className = 'theia-input theia-ChatInput';
|
|
109
|
+
this.chatInput.placeholder = 'Ask about a terminal command...';
|
|
110
|
+
this.chatInput.onkeydown = event => {
|
|
111
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
if (!this.haveResult) {
|
|
114
|
+
this.send();
|
|
115
|
+
} else {
|
|
116
|
+
this.terminalWidget.sendText(this.chatResultParagraph.innerText);
|
|
117
|
+
this.dispose();
|
|
118
|
+
}
|
|
119
|
+
} else if (event.key === 'Escape') {
|
|
120
|
+
this.dispose();
|
|
121
|
+
} else if (event.key === 'ArrowUp' && this.haveResult) {
|
|
122
|
+
this.updateChatResult(this.getNextCommandIndex(1));
|
|
123
|
+
} else if (event.key === 'ArrowDown' && this.haveResult) {
|
|
124
|
+
this.updateChatResult(this.getNextCommandIndex(-1));
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
this.chatInputContainer.appendChild(this.chatInput);
|
|
128
|
+
|
|
129
|
+
const chatInputOptionsContainer = document.createElement('div');
|
|
130
|
+
const chatInputOptionsSpan = document.createElement('span');
|
|
131
|
+
chatInputOptionsSpan.className = 'codicon codicon-send option';
|
|
132
|
+
chatInputOptionsSpan.title = 'Send';
|
|
133
|
+
chatInputOptionsSpan.onclick = () => this.send();
|
|
134
|
+
chatInputOptionsContainer.appendChild(chatInputOptionsSpan);
|
|
135
|
+
this.chatInputContainer.appendChild(chatInputOptionsContainer);
|
|
136
|
+
|
|
137
|
+
this.chatContainer.appendChild(this.chatInputContainer);
|
|
138
|
+
|
|
139
|
+
terminalWidget.node.appendChild(this.chatContainer);
|
|
140
|
+
|
|
141
|
+
this.chatInput.focus();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
protected async send(): Promise<void> {
|
|
145
|
+
const userRequest = this.chatInput.value;
|
|
146
|
+
if (userRequest) {
|
|
147
|
+
this.chatInput.value = '';
|
|
148
|
+
|
|
149
|
+
this.chatResultParagraph.innerText = 'Loading';
|
|
150
|
+
this.chatResultParagraph.className = 'loading';
|
|
151
|
+
|
|
152
|
+
const cwd = (await this.terminalWidget.cwd).toString();
|
|
153
|
+
const processInfo = await this.terminalWidget.processInfo;
|
|
154
|
+
const shell = processInfo.executable;
|
|
155
|
+
const recentTerminalContents = this.getRecentTerminalCommands();
|
|
156
|
+
|
|
157
|
+
this.commands = await this.terminalAgent.getCommands(userRequest, cwd, shell, recentTerminalContents);
|
|
158
|
+
|
|
159
|
+
if (this.commands.length > 0) {
|
|
160
|
+
this.chatResultParagraph.className = 'command';
|
|
161
|
+
this.chatResultParagraph.innerText = this.commands[0];
|
|
162
|
+
this.chatInput.placeholder = 'Hit enter to confirm';
|
|
163
|
+
if (this.commands.length > 1) {
|
|
164
|
+
this.chatInput.placeholder += ' or use ⇅ to show alternatives...';
|
|
165
|
+
}
|
|
166
|
+
this.haveResult = true;
|
|
167
|
+
} else {
|
|
168
|
+
this.chatResultParagraph.className = '';
|
|
169
|
+
this.chatResultParagraph.innerText = 'No results';
|
|
170
|
+
this.chatInput.placeholder = 'Try again...';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected getRecentTerminalCommands(): string[] {
|
|
176
|
+
const maxLines = 100;
|
|
177
|
+
return this.terminalWidget.buffer.getLines(0,
|
|
178
|
+
this.terminalWidget.buffer.length > maxLines ? maxLines : this.terminalWidget.buffer.length
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
protected getNextCommandIndex(step: number): number {
|
|
183
|
+
const currentIndex = this.commands.indexOf(this.chatResultParagraph.innerText);
|
|
184
|
+
const nextIndex = (currentIndex + step + this.commands.length) % this.commands.length;
|
|
185
|
+
return nextIndex;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected updateChatResult(index: number): void {
|
|
189
|
+
this.chatResultParagraph.innerText = this.commands[index];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
protected dispose(): void {
|
|
193
|
+
this.chatInput.value = '';
|
|
194
|
+
this.terminalWidget.node.removeChild(this.chatContainer);
|
|
195
|
+
this.terminalWidget.getTerminal().focus();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { Agent } from '@theia/ai-core/lib/common';
|
|
18
|
+
import { CommandContribution, MenuContribution } from '@theia/core';
|
|
19
|
+
import { KeybindingContribution } from '@theia/core/lib/browser';
|
|
20
|
+
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
21
|
+
import { AiTerminalAgent } from './ai-terminal-agent';
|
|
22
|
+
import { AiTerminalCommandContribution } from './ai-terminal-contribution';
|
|
23
|
+
|
|
24
|
+
import '../../src/browser/style/ai-terminal.css';
|
|
25
|
+
|
|
26
|
+
export default new ContainerModule(bind => {
|
|
27
|
+
bind(AiTerminalCommandContribution).toSelf().inSingletonScope();
|
|
28
|
+
for (const identifier of [CommandContribution, MenuContribution, KeybindingContribution]) {
|
|
29
|
+
bind(identifier).toService(AiTerminalCommandContribution);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bind(AiTerminalAgent).toSelf().inSingletonScope();
|
|
33
|
+
bind(Agent).toService(AiTerminalAgent);
|
|
34
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
.ai-terminal-chat-container {
|
|
2
|
+
position: absolute;
|
|
3
|
+
bottom: 0;
|
|
4
|
+
left: 50%;
|
|
5
|
+
transform: translateX(-50%);
|
|
6
|
+
width: 100%;
|
|
7
|
+
max-width: 500px;
|
|
8
|
+
padding: 10px;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
background: var(--theia-menu-background);
|
|
11
|
+
color: var(--theia-menu-foreground);
|
|
12
|
+
margin-bottom: 12px;
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
align-items: center;
|
|
16
|
+
border: 1px solid var(--theia-menu-border);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.ai-terminal-chat-container .closeButton {
|
|
20
|
+
position: absolute;
|
|
21
|
+
top: 1em;
|
|
22
|
+
right: 1em;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.ai-terminal-chat-container .closeButton:hover {
|
|
27
|
+
color: var(--theia-menu-foreground);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ai-terminal-chat-result {
|
|
31
|
+
width: 100%;
|
|
32
|
+
margin-bottom: 10px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ai-terminal-chat-input-container {
|
|
36
|
+
width: 100%;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.ai-terminal-chat-input-container textarea {
|
|
42
|
+
flex-grow: 1;
|
|
43
|
+
height: 36px;
|
|
44
|
+
background-color: var(--theia-input-background);
|
|
45
|
+
border-radius: 4px;
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
padding: 8px;
|
|
48
|
+
resize: none;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
line-height: 1.3rem;
|
|
51
|
+
margin-right: 10px; /* Add some space between textarea and button */
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ai-terminal-chat-input-container .option {
|
|
55
|
+
width: 21px;
|
|
56
|
+
height: 21px;
|
|
57
|
+
display: inline-block;
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
user-select: none;
|
|
60
|
+
background-repeat: no-repeat;
|
|
61
|
+
background-position: center;
|
|
62
|
+
border: var(--theia-border-width) solid transparent;
|
|
63
|
+
opacity: 0.7;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ai-terminal-chat-input-container .option:hover {
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@keyframes dots {
|
|
72
|
+
0%,
|
|
73
|
+
20% {
|
|
74
|
+
content: "";
|
|
75
|
+
}
|
|
76
|
+
40% {
|
|
77
|
+
content: ".";
|
|
78
|
+
}
|
|
79
|
+
60% {
|
|
80
|
+
content: "..";
|
|
81
|
+
}
|
|
82
|
+
80%,
|
|
83
|
+
100% {
|
|
84
|
+
content: "...";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
.ai-terminal-chat-result p.loading::after {
|
|
88
|
+
content: "";
|
|
89
|
+
animation: dots 1s steps(1, end) infinite;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ai-terminal-chat-result p.command {
|
|
93
|
+
font-family: "Droid Sans Mono", "monospace", monospace;
|
|
94
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH and others.
|
|
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
|
+
/* note: this bogus test file is required so that
|
|
18
|
+
we are able to run mocha unit tests on this
|
|
19
|
+
package, without having any actual unit tests in it.
|
|
20
|
+
This way a coverage report will be generated,
|
|
21
|
+
showing 0% coverage, instead of no report.
|
|
22
|
+
This file can be removed once we have real unit
|
|
23
|
+
tests in place. */
|
|
24
|
+
|
|
25
|
+
describe('ai-terminal package', () => {
|
|
26
|
+
|
|
27
|
+
it('support code coverage statistics', () => true);
|
|
28
|
+
});
|