Skip to content

Commit d8887e0

Browse files
Merge branch 'main' into dallin/limit-glob-output
2 parents 2b5b09e + 75d351a commit d8887e0

File tree

108 files changed

+1213
-367
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+1213
-367
lines changed

core/commands/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function slashFromCustomCommand(
2525
config,
2626
selectedCode,
2727
fetch,
28+
abortController,
2829
}) {
2930
// Render prompt template
3031
let renderedPrompt: string;
@@ -63,7 +64,7 @@ export function slashFromCustomCommand(
6364

6465
for await (const chunk of llm.streamChat(
6566
messages,
66-
new AbortController().signal,
67+
abortController.signal,
6768
completionOptions,
6869
)) {
6970
yield renderChatMessage(chunk);

core/commands/slash/commit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { renderChatMessage } from "../../util/messageContent.js";
44
const CommitMessageCommand: SlashCommand = {
55
name: "commit",
66
description: "Generate a commit message for current changes",
7-
run: async function* ({ ide, llm, params }) {
7+
run: async function* ({ ide, llm, params, abortController }) {
88
const includeUnstaged = params?.includeUnstaged ?? false;
99
const diff = await ide.getDiff(includeUnstaged);
1010

@@ -16,7 +16,7 @@ const CommitMessageCommand: SlashCommand = {
1616
const prompt = `${diff.join("\n")}\n\nGenerate a commit message for the above set of changes. First, give a single sentence, no more than 80 characters. Then, after 2 line breaks, give a list of no more than 5 short bullet points, each no more than 40 characters. Output nothing except for the commit message, and don't surround it in quotes.`;
1717
for await (const chunk of llm.streamChat(
1818
[{ role: "user", content: prompt }],
19-
new AbortController().signal,
19+
abortController.signal,
2020
)) {
2121
yield renderChatMessage(chunk);
2222
}

core/commands/slash/draftIssue.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Body:\n\n`;
2424
const DraftIssueCommand: SlashCommand = {
2525
name: "issue",
2626
description: "Draft a GitHub issue",
27-
run: async function* ({ input, llm, history, params }) {
27+
run: async function* ({ input, llm, history, params, abortController }) {
2828
if (params?.repositoryUrl === undefined) {
2929
yield "This command requires a repository URL to be set in the config file.";
3030
return;
@@ -46,7 +46,7 @@ const DraftIssueCommand: SlashCommand = {
4646

4747
for await (const chunk of llm.streamChat(
4848
messages,
49-
new AbortController().signal,
49+
abortController.signal,
5050
)) {
5151
body += chunk.content;
5252
yield renderChatMessage(chunk);

core/commands/slash/mcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function constructMcpSlashCommand(
6060

6161
for await (const chunk of context.llm.streamChat(
6262
messages,
63-
new AbortController().signal,
63+
context.abortController.signal,
6464
context.completionOptions,
6565
)) {
6666
yield renderChatMessage(chunk);

core/commands/slash/onboard.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ const MAX_EXPLORE_DEPTH = 2;
4141
const OnboardSlashCommand: SlashCommand = {
4242
name: "onboard",
4343
description: "Familiarize yourself with the codebase",
44-
run: async function* ({ llm, ide }) {
44+
run: async function* ({ llm, ide, abortController }) {
4545
const [workspaceDir] = await ide.getWorkspaceDirs();
4646

4747
const context = await gatherProjectContext(workspaceDir, ide);
4848
const prompt = createOnboardingPrompt(context);
4949

5050
for await (const chunk of llm.streamChat(
5151
[{ role: "user", content: prompt }],
52-
new AbortController().signal,
52+
abortController.signal,
5353
)) {
5454
yield renderChatMessage(chunk);
5555
}

core/commands/slash/review.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ function getLastUserHistory(history: ChatMessage[]): string {
3838
const ReviewMessageCommand: SlashCommand = {
3939
name: "review",
4040
description: "Review code and give feedback",
41-
run: async function* ({ llm, history }) {
41+
run: async function* ({ llm, history, abortController }) {
4242
const reviewText = getLastUserHistory(history).replace("\\review", "");
4343

4444
const content = `${prompt} \r\n ${reviewText}`;
4545

4646
for await (const chunk of llm.streamChat(
4747
[{ role: "user", content: content }],
48-
new AbortController().signal,
48+
abortController.signal,
4949
)) {
5050
yield renderChatMessage(chunk);
5151
}

core/config/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ declare global {
719719
720720
getPinnedFiles(): Promise<string[]>;
721721
722-
getSearchResults(query: string): Promise<string>;
722+
getSearchResults(query: string, maxResults?: number): Promise<string>;
723723
724724
subprocess(command: string, cwd?: string): Promise<[string, string]>;
725725

core/context/providers/SearchContextProvider.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import { formatGrepSearchResults } from "../../util/grepSearch.js";
77
import { BaseContextProvider } from "../index.js";
88

9+
const DEFAULT_MAX_SEARCH_CONTEXT_RESULTS = 200;
910
class SearchContextProvider extends BaseContextProvider {
1011
static description: ContextProviderDescription = {
1112
title: "search",
@@ -19,8 +20,12 @@ class SearchContextProvider extends BaseContextProvider {
1920
query: string,
2021
extras: ContextProviderExtras,
2122
): Promise<ContextItem[]> {
22-
const results = await extras.ide.getSearchResults(query);
23-
const formatted = formatGrepSearchResults(results);
23+
const results = await extras.ide.getSearchResults(
24+
query,
25+
this.options?.maxResults ?? DEFAULT_MAX_SEARCH_CONTEXT_RESULTS,
26+
);
27+
// Note, search context provider will not truncate result chars, but will limit number of results
28+
const { formatted } = formatGrepSearchResults(results);
2429
return [
2530
{
2631
description: "Search results",

core/core.ts

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ import {
4242
IdeSettings,
4343
ModelDescription,
4444
RangeInFile,
45+
ToolCall,
4546
type ContextItem,
4647
type ContextItemId,
4748
type IDE,
4849
} from ".";
4950

50-
import { ConfigYaml } from "@continuedev/config-yaml";
51+
import { BLOCK_TYPES, ConfigYaml } from "@continuedev/config-yaml";
5152
import { getDiffFn, GitDiffCache } from "./autocomplete/snippets/gitDiffCache";
5253
import { isLocalDefinitionFile } from "./config/loadLocalAssistants";
5354
import {
@@ -58,6 +59,7 @@ import {
5859
import { createNewWorkspaceBlockFile } from "./config/workspace/workspaceBlocks";
5960
import { MCPManagerSingleton } from "./context/mcp/MCPManagerSingleton";
6061
import { setMdmLicenseKey } from "./control-plane/mdm/mdm";
62+
import { ApplyAbortManager } from "./edit/applyAbortManager";
6163
import { streamDiffLines } from "./edit/streamDiffLines";
6264
import { shouldIgnore } from "./indexing/shouldIgnore";
6365
import { walkDirCache } from "./indexing/walkDir";
@@ -67,7 +69,6 @@ import { llmStreamChat } from "./llm/streamChat";
6769
import type { FromCoreProtocol, ToCoreProtocol } from "./protocol";
6870
import { OnboardingModes } from "./protocol/core";
6971
import type { IMessenger, Message } from "./protocol/messenger";
70-
import { StreamAbortManager } from "./util/abortManager";
7172
import { getUriPathBasename } from "./util/uri";
7273

7374
const hasRulesFiles = (uris: string[]): boolean => {
@@ -147,6 +148,18 @@ export class Core {
147148

148149
MCPManagerSingleton.getInstance().onConnectionsRefreshed = () => {
149150
void this.configHandler.reloadConfig();
151+
152+
// Refresh @mention dropdown submenu items for MCP providers
153+
const mcpManager = MCPManagerSingleton.getInstance();
154+
const mcpProviderNames = Array.from(mcpManager.connections.keys()).map(
155+
(mcpId) => `mcp-${mcpId}`,
156+
);
157+
158+
if (mcpProviderNames.length > 0) {
159+
this.messenger.send("refreshSubmenuItems", {
160+
providers: mcpProviderNames,
161+
});
162+
}
150163
};
151164

152165
this.codeBaseIndexer = new CodebaseIndexer(
@@ -512,6 +525,11 @@ export class Core {
512525
throw new Error("No model selected");
513526
}
514527

528+
const abortManager = ApplyAbortManager.getInstance();
529+
const abortController = abortManager.get(
530+
data.fileUri ?? "current-file-stream",
531+
); // not super important since currently cancelling apply will cancel all streams it's one file at a time
532+
515533
return streamDiffLines({
516534
highlighted: data.highlighted,
517535
prefix: data.prefix,
@@ -525,13 +543,13 @@ export class Core {
525543
language: data.language,
526544
onlyOneInsertion: false,
527545
overridePrompt: undefined,
528-
abortControllerId: data.fileUri ?? "current-file-stream", // not super important since currently cancelling apply will cancel all streams it's one file at a time
546+
abortController,
529547
});
530548
});
531549

532550
on("cancelApply", async (msg) => {
533-
const abortManager = StreamAbortManager.getInstance();
534-
abortManager.clear();
551+
const abortManager = ApplyAbortManager.getInstance();
552+
abortManager.clear(); // for now abort all streams
535553
});
536554

537555
on("onboarding/complete", this.handleCompleteOnboarding.bind(this));
@@ -738,44 +756,9 @@ export class Core {
738756
return { url };
739757
});
740758

741-
on("tools/call", async ({ data: { toolCall } }) => {
742-
const { config } = await this.configHandler.loadConfig();
743-
if (!config) {
744-
throw new Error("Config not loaded");
745-
}
746-
747-
const tool = config.tools.find(
748-
(t) => t.function.name === toolCall.function.name,
749-
);
750-
751-
if (!tool) {
752-
throw new Error(`Tool ${toolCall.function.name} not found`);
753-
}
754-
755-
if (!config.selectedModelByRole.chat) {
756-
throw new Error("No chat model selected");
757-
}
758-
759-
// Define a callback for streaming output updates
760-
const onPartialOutput = (params: {
761-
toolCallId: string;
762-
contextItems: ContextItem[];
763-
}) => {
764-
this.messenger.send("toolCallPartialOutput", params);
765-
};
766-
767-
return await callTool(tool, toolCall, {
768-
config,
769-
ide: this.ide,
770-
llm: config.selectedModelByRole.chat,
771-
fetch: (url, init) =>
772-
fetchwithRequestOptions(url, init, config.requestOptions),
773-
tool,
774-
toolCallId: toolCall.id,
775-
onPartialOutput,
776-
codeBaseIndexer: this.codeBaseIndexer,
777-
});
778-
});
759+
on("tools/call", async ({ data: { toolCall } }) =>
760+
this.handleToolCall(toolCall),
761+
);
779762

780763
on("isItemTooBig", async ({ data: { item } }) => {
781764
return this.isItemTooBig(item);
@@ -800,6 +783,45 @@ export class Core {
800783
});
801784
}
802785

786+
private async handleToolCall(toolCall: ToolCall) {
787+
const { config } = await this.configHandler.loadConfig();
788+
if (!config) {
789+
throw new Error("Config not loaded");
790+
}
791+
792+
const tool = config.tools.find(
793+
(t) => t.function.name === toolCall.function.name,
794+
);
795+
796+
if (!tool) {
797+
throw new Error(`Tool ${toolCall.function.name} not found`);
798+
}
799+
800+
if (!config.selectedModelByRole.chat) {
801+
throw new Error("No chat model selected");
802+
}
803+
804+
// Define a callback for streaming output updates
805+
const onPartialOutput = (params: {
806+
toolCallId: string;
807+
contextItems: ContextItem[];
808+
}) => {
809+
this.messenger.send("toolCallPartialOutput", params);
810+
};
811+
812+
return await callTool(tool, toolCall, {
813+
config,
814+
ide: this.ide,
815+
llm: config.selectedModelByRole.chat,
816+
fetch: (url, init) =>
817+
fetchwithRequestOptions(url, init, config.requestOptions),
818+
tool,
819+
toolCallId: toolCall.id,
820+
onPartialOutput,
821+
codeBaseIndexer: this.codeBaseIndexer,
822+
});
823+
}
824+
803825
private async isItemTooBig(item: ContextItemWithId) {
804826
const { config } = await this.configHandler.loadConfig();
805827
if (!config) {
@@ -887,7 +909,12 @@ export class Core {
887909
uri.endsWith(".prompt") ||
888910
uri.endsWith(SYSTEM_PROMPT_DOT_FILE) ||
889911
(uri.includes(".continue") && uri.endsWith(".yaml")) ||
890-
uri.endsWith(RULES_MARKDOWN_FILENAME)
912+
uri.endsWith(RULES_MARKDOWN_FILENAME) ||
913+
BLOCK_TYPES.some(
914+
(blockType) =>
915+
uri.includes(`.continue/${blockType}`) ||
916+
uri.includes(`.continue\\${blockType}`),
917+
)
891918
) {
892919
await this.configHandler.reloadConfig();
893920
} else if (

core/util/abortManager.ts renamed to core/edit/applyAbortManager.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
export class StreamAbortManager {
2-
private static instance: StreamAbortManager;
1+
export class ApplyAbortManager {
2+
private static instance: ApplyAbortManager;
33
private controllers: Map<string, AbortController>;
44

55
private constructor() {
66
this.controllers = new Map();
77
}
88

9-
public static getInstance(): StreamAbortManager {
10-
if (!StreamAbortManager.instance) {
11-
StreamAbortManager.instance = new StreamAbortManager();
9+
public static getInstance(): ApplyAbortManager {
10+
if (!ApplyAbortManager.instance) {
11+
ApplyAbortManager.instance = new ApplyAbortManager();
1212
}
13-
return StreamAbortManager.instance;
13+
return ApplyAbortManager.instance;
1414
}
1515

1616
public get(id: string): AbortController {

core/edit/lazy/applyCodeBlock.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export async function applyCodeBlock(
1616
newLazyFile: string,
1717
filename: string,
1818
llm: ILLM,
19+
abortController: AbortController,
1920
): Promise<{
2021
isInstantApply: boolean;
2122
diffLinesGenerator: AsyncGenerator<DiffLine>;
@@ -51,6 +52,12 @@ export async function applyCodeBlock(
5152

5253
return {
5354
isInstantApply: false,
54-
diffLinesGenerator: streamLazyApply(oldFile, filename, newLazyFile, llm),
55+
diffLinesGenerator: streamLazyApply(
56+
oldFile,
57+
filename,
58+
newLazyFile,
59+
llm,
60+
abortController,
61+
),
5562
};
5663
}

core/edit/lazy/replace.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export async function* getReplacementWithLlm(
5959
linesBefore: string[],
6060
linesAfter: string[],
6161
llm: ILLM,
62+
abortController: AbortController,
6263
): AsyncGenerator<string> {
6364
const userPrompt = dedent`
6465
ORIGINAL CODE:
@@ -88,7 +89,7 @@ export async function* getReplacementWithLlm(
8889
{ role: "user", content: userPrompt },
8990
{ role: "assistant", content: assistantPrompt },
9091
],
91-
new AbortController().signal,
92+
abortController.signal,
9293
);
9394

9495
let lines = streamLines(completion);

0 commit comments

Comments
 (0)