Skip to content

Commit 488622b

Browse files
authored
feat: Support agent skills in agent mode and add preferences settings (#133)
1 parent 9aeae2b commit 488622b

23 files changed

Lines changed: 559 additions & 261 deletions

File tree

.github/skills/ui-action/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,4 @@ action is usable from every probe.
296296
### Keep one probe focused
297297

298298
Each JSON script represents one test case. Split unrelated behaviours into
299-
separate probes so a single `FAILED-step…` screenshot tells you what broke.
299+
separate probes so a single `FAILED-step…` screenshot tells you what broke.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ MCP support enables integrating external tools and services into Copilot workflo
6161
- **Custom Agents** allow users to create personalized agents with specific instructions and behaviors.
6262
- **Isolated Subagents** can be spawned by the main agent to handle specific tasks or contexts independently.
6363
- **Plan Agent** can generate multi-step plans to accomplish complex tasks, breaking them down into manageable actions.
64+
- **Skills** are reusable, specialized AI assistant templates that enrich chat context in Agent Mode. Skills are defined as `SKILL.md` files and can be scoped to a workspace or shared globally.
65+
66+
- Creating Skills
67+
68+
Place a `SKILL.md` file in any of these directories:
69+
70+
- **Project-scoped:** `.github/skills/<skill-name>/`, `.claude/skills/<skill-name>/`, `.agents/skills/<skill-name>/`
71+
- **User-scoped (global):** `~/.copilot/skills/<skill-name>/`, `~/.claude/skills/<skill-name>/`, `~/.agents/skills/<skill-name>/`
72+
73+
Each `SKILL.md` file can include YAML front matter with metadata (name, description) followed by Markdown content that provides domain knowledge, workflows, or instructions for the AI assistant.
74+
75+
Skills are automatically discovered and available in Agent Mode. You can enable or disable skills in **Window → Preferences → Copilot → Chat → Enable Skills**.
6476

6577
For other available features in Eclipse, see the [Copilot feature matrix](https://docs.github.com/en/copilot/reference/copilot-feature-matrix?tool=eclipse).
6678

com.microsoft.copilot.eclipse.core/copilot-agent/package-lock.json

Lines changed: 33 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

com.microsoft.copilot.eclipse.core/copilot-agent/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
"postinstall": "node copy-binaries.js"
1313
},
1414
"dependencies": {
15-
"@github/copilot-language-server": "1.482.0",
16-
"@github/copilot-language-server-win32-x64": "1.482.0",
17-
"@github/copilot-language-server-darwin-x64": "1.482.0",
18-
"@github/copilot-language-server-darwin-arm64": "1.482.0",
19-
"@github/copilot-language-server-linux-x64": "1.482.0",
20-
"@github/copilot-language-server-linux-arm64": "1.482.0"
15+
"@github/copilot-language-server": "1.484.0",
16+
"@github/copilot-language-server-win32-x64": "1.484.0",
17+
"@github/copilot-language-server-darwin-x64": "1.484.0",
18+
"@github/copilot-language-server-darwin-arm64": "1.484.0",
19+
"@github/copilot-language-server-linux-x64": "1.484.0",
20+
"@github/copilot-language-server-linux-arm64": "1.484.0"
2121
}
2222
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ private Constants() {
2828
public static final String WORKSPACE_CONTEXT_ENABLED = "workspaceContextEnabled";
2929
public static final String SUB_AGENT_ENABLED = "subAgentEnabled";
3030
public static final String AGENT_MAX_REQUESTS = "agentMaxRequests";
31+
public static final String ENABLE_SKILLS = "enableSkills";
3132
public static final String TRANSCRIPT_SUBDIR = ".copilot/eclipse";
3233
public static final String MCP = "mcp";
3334
public static final String MCP_REGISTRY_URL = "mcpRegistryUrl";

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/events/CopilotEventConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,10 @@ public class CopilotEventConstants {
160160
* Event when a rate limit warning is received from the language server.
161161
*/
162162
public static final String TOPIC_RATE_LIMIT_WARNING = TOPIC_CHAT + "RATE_LIMIT_WARNING";
163+
164+
/**
165+
* Event when custom prompts, skills, agents, or instructions change on the language server. Clients should re-fetch
166+
* conversation templates on receipt.
167+
*/
168+
public static final String TOPIC_CHAT_DID_CHANGE_CUSTOMIZATION_FILES = TOPIC_CHAT + "DID_CHANGE_CUSTOMIZATION_FILES";
163169
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ public CompletableFuture<Object[]> getConversationContext(ConversationContextPar
140140
public CompletableFuture<Object> invokeClientTool(InvokeClientToolParams params) {
141141
return CompletableFuture.supplyAsync(() -> {
142142
try {
143-
CompletableFuture<LanguageModelToolResult[]> toolFuture =
144-
CopilotCore.getPlugin().getChatEventsManager().invokeAgentTool(params);
143+
CompletableFuture<LanguageModelToolResult[]> toolFuture = CopilotCore.getPlugin().getChatEventsManager()
144+
.invokeAgentTool(params);
145145
if (toolFuture == null) {
146-
CopilotCore.LOGGER.error(
147-
new IllegalStateException("invokeAgentTool returned null for tool: " + params.getName()));
146+
CopilotCore.LOGGER
147+
.error(new IllegalStateException("invokeAgentTool returned null for tool: " + params.getName()));
148148
LanguageModelToolResult errorResult = new LanguageModelToolResult();
149149
errorResult.addContent("Failed to invoke the tool: tool invocation returned null");
150150
errorResult.setStatus(ToolInvocationStatus.error);
@@ -182,6 +182,8 @@ public CompletableFuture<Object[]> confirmClientTool(InvokeClientToolConfirmatio
182182
});
183183
}
184184

185+
// TODO: Should remove workspace-root folder as the projects are not directly under it in Eclipse, and can cause
186+
// confusion in CLS.
185187
@Override
186188
public CompletableFuture<List<WorkspaceFolder>> workspaceFolders() {
187189
// Ideally, we should return each IProject as a workspace folder, but given that when
@@ -259,9 +261,30 @@ public void onRateLimitWarning(RateLimitWarningParams params) {
259261
}
260262

261263
/**
262-
* Handles the Dynamic OAuth request for MCP.
263-
* Shows a dialog with multiple input fields and returns the user's input values.
264-
* Returns null if the user cancels the request.
264+
* Notify when custom skills change (global or workspace). Signal-only; clients re-fetch templates.
265+
*/
266+
@JsonNotification("copilot/customSkill/didChange")
267+
public void onDidChangeCustomSkill(Object params) {
268+
notifyCustomizationFilesChanged();
269+
}
270+
271+
/**
272+
* Notify when custom prompts change (global or workspace). Signal-only; clients re-fetch templates.
273+
*/
274+
@JsonNotification("copilot/customPrompt/didChange")
275+
public void onDidChangeCustomPrompt(Object params) {
276+
notifyCustomizationFilesChanged();
277+
}
278+
279+
private void notifyCustomizationFilesChanged() {
280+
if (eventBroker != null) {
281+
eventBroker.post(CopilotEventConstants.TOPIC_CHAT_DID_CHANGE_CUSTOMIZATION_FILES, null);
282+
}
283+
}
284+
285+
/**
286+
* Handles the Dynamic OAuth request for MCP. Shows a dialog with multiple input fields and returns the user's input
287+
* values. Returns null if the user cancels the request.
265288
*/
266289
@JsonRequest("copilot/dynamicOAuth")
267290
public CompletableFuture<Map<String, String>> mcpOauth(McpOauthRequest request) {
@@ -307,13 +330,11 @@ public void onDidChangePolicy(DidChangePolicyParams params) {
307330
}
308331
if (flags.isSubAgentPolicyEnabled() != params.isSubAgentEnabled()) {
309332
flags.setSubAgentPolicyEnabled(params.isSubAgentEnabled());
310-
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_SUB_AGENT_POLICY,
311-
params.isSubAgentEnabled());
333+
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_SUB_AGENT_POLICY, params.isSubAgentEnabled());
312334
}
313335
if (flags.isCustomAgentPolicyEnabled() != params.isCustomAgentEnabled()) {
314336
flags.setCustomAgentPolicyEnabled(params.isCustomAgentEnabled());
315-
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_CUSTOM_AGENT_POLICY,
316-
params.isCustomAgentEnabled());
337+
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_CUSTOM_AGENT_POLICY, params.isCustomAgentEnabled());
317338
}
318339
}
319340
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
3030
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
3131
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
32+
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplatesParams;
3233
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTurnParams;
3334
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotModel;
3435
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@@ -140,9 +141,11 @@ public interface CopilotLanguageServer extends LanguageServer {
140141

141142
/**
142143
* List conversation templates.
144+
*
145+
* @param params includes workspace folders for discovering workspace-specific prompt files and skills
143146
*/
144147
@JsonRequest("conversation/templates")
145-
CompletableFuture<ConversationTemplate[]> listTemplates(NullParams param);
148+
CompletableFuture<ConversationTemplate[]> listTemplates(ConversationTemplatesParams params);
146149

147150
/**
148151
* List conversation modes.

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.eclipse.lsp4j.ProgressParams;
2121
import org.eclipse.lsp4j.Range;
2222
import org.eclipse.lsp4j.TextDocumentIdentifier;
23+
import org.eclipse.lsp4j.WorkspaceFolder;
2324
import org.eclipse.lsp4j.jsonrpc.Endpoint;
2425
import org.eclipse.lsp4j.jsonrpc.messages.Either;
2526
import org.eclipse.lsp4j.services.LanguageServer;
@@ -46,6 +47,7 @@
4647
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
4748
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
4849
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
50+
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplatesParams;
4951
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTurnParams;
5052
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotModel;
5153
import com.microsoft.copilot.eclipse.core.lsp.protocol.CopilotStatusResult;
@@ -391,10 +393,12 @@ public CompletableFuture<ChatTurnResult> addConversationTurn(String workDoneToke
391393

392394
/**
393395
* List the conversation templates.
396+
*
397+
* @param workspaceFolders workspace folders for discovering workspace-specific prompt files and skills
394398
*/
395-
public CompletableFuture<ConversationTemplate[]> listConversationTemplates() {
399+
public CompletableFuture<ConversationTemplate[]> listConversationTemplates(List<WorkspaceFolder> workspaceFolders) {
396400
Function<LanguageServer, CompletableFuture<ConversationTemplate[]>> fn = server -> {
397-
return ((CopilotLanguageServer) server).listTemplates(new NullParams());
401+
return ((CopilotLanguageServer) server).listTemplates(new ConversationTemplatesParams(workspaceFolders));
398402
};
399403
return this.languageServerWrapper.execute(fn);
400404
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/ConversationTemplate.java

Lines changed: 7 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,14 @@
44
package com.microsoft.copilot.eclipse.core.lsp.protocol;
55

66
import java.util.List;
7-
import java.util.Objects;
8-
9-
import org.apache.commons.lang3.builder.ToStringBuilder;
107

118
/**
12-
* Get the templates.
9+
* Represents a conversation template returned by the language server.
1310
*/
14-
public class ConversationTemplate {
15-
16-
17-
private String id;
18-
private String description;
19-
private String shortDescription;
20-
private List<String> scopes;
21-
22-
public String getId() {
23-
return id;
24-
}
25-
26-
public void setId(String id) {
27-
this.id = id;
28-
}
29-
30-
public String getDescription() {
31-
return description;
32-
}
33-
34-
public void setDescription(String description) {
35-
this.description = description;
36-
}
37-
38-
public String getShortDescription() {
39-
return shortDescription;
40-
}
41-
42-
public void setShortDescription(String shortDescription) {
43-
this.shortDescription = shortDescription;
44-
}
45-
46-
public List<String> getScopes() {
47-
return scopes;
48-
}
49-
50-
public void setScopes(List<String> scopes) {
51-
this.scopes = scopes;
52-
}
53-
54-
@Override
55-
public int hashCode() {
56-
return Objects.hash(id, description, shortDescription, scopes);
57-
}
58-
59-
@Override
60-
public boolean equals(Object obj) {
61-
if (this == obj) {
62-
return true;
63-
}
64-
if (obj == null || getClass() != obj.getClass()) {
65-
return false;
66-
}
67-
ConversationTemplate that = (ConversationTemplate) obj;
68-
return Objects.equals(id, that.id) && Objects.equals(description, that.description)
69-
&& Objects.equals(shortDescription, that.shortDescription) && Objects.equals(scopes, that.scopes);
70-
}
71-
72-
@Override
73-
public String toString() {
74-
ToStringBuilder builder = new ToStringBuilder(this);
75-
builder.append("id", id);
76-
builder.append("description", description);
77-
builder.append("shortDescription", shortDescription);
78-
builder.append("scopes", scopes);
79-
return builder.toString();
80-
}
11+
public record ConversationTemplate(
12+
String id,
13+
String description,
14+
String shortDescription,
15+
List<String> scopes,
16+
TemplateSource source) {
8117
}

0 commit comments

Comments
 (0)