Skip to content

Commit e26d189

Browse files
authored
Merge branch 'main' into feat/watsonx_reranker_integration
2 parents 11fdace + 46ffc40 commit e26d189

File tree

171 files changed

+4408
-2288
lines changed

Some content is hidden

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

171 files changed

+4408
-2288
lines changed

.continueignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ docs/docs/languages
55
.vscode/
66
.archive/
77
**/*.scm
8-
**/*.diff
8+
**/*.diff
9+
.continue/

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @continuedev/continue-code-reviewers

.github/workflows/pr_checks.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ jobs:
275275
cd core
276276
npm test
277277
env:
278-
IGNORE_API_KEY_TESTS: ${{ github.event.pull_request.head.repo.fork == true && github.actor == 'dependabot[bot]' }}
278+
IGNORE_API_KEY_TESTS: ${{ github.event.pull_request.head.repo.fork == true || github.actor == 'dependabot[bot]' }}
279279
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
280280
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
281281
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -420,8 +420,7 @@ jobs:
420420
id: test-extensions-cache
421421
with:
422422
path: extensions/vscode/e2e/.test-extensions
423-
# package.json used as the key because that's where the download script is defined
424-
key: vscode-test-extensions-${{ hashFiles('extensions/vscode/package.json') }}
423+
key: CONSTANT
425424

426425
- name: Download build artifact
427426
uses: actions/download-artifact@v4
@@ -460,7 +459,7 @@ jobs:
460459
- name: Run e2e tests
461460
run: |
462461
cd extensions/vscode
463-
IGNORE_SSH_TESTS="${{ github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]' }}" TEST_FILE="${{ matrix.test_file }}" npm run ${{ matrix.command }}
462+
IGNORE_SSH_TESTS="${{ github.event.pull_request.head.repo.fork == true || github.actor == 'dependabot[bot]' }}" TEST_FILE="${{ matrix.test_file }}" npm run ${{ matrix.command }}
464463
env:
465464
DISPLAY: :99
466465

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,5 @@ extensions/.continue-debug/
168168

169169
*.vsix
170170

171-
.continuerules
171+
.continuerules
172+
**/.continue/assistants/

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ gui/dist
1414
**/.continueignore
1515
CHANGELOG.md
1616
**/continue_tutorial.py
17-
**/node_modules
17+
**/node_modules
18+
**/CODEOWNERS

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<div align="center">
1010

11-
**[Continue](https://docs.continue.dev) enables to developers to create, share, and use custom AI code assistants with our open-source [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) and [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions and [hub of models, rules, prompts, docs, and other building blocks](https://hub.continue.dev)**
11+
**[Continue](https://docs.continue.dev) enables developers to create, share, and use custom AI code assistants with our open-source [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) and [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions and [hub of models, rules, prompts, docs, and other building blocks](https://hub.continue.dev)**
1212

1313
</div>
1414

binary/package-lock.json

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

core/autocomplete/CompletionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export class CompletionProvider {
262262

263263
// When using the JetBrains extension, Mark as displayed
264264
const ideType = (await this.ide.getIdeInfo()).ideType;
265-
if (ideType === "jetbrains") {
265+
if (ideType.toLowerCase() === "jetbrains") {
266266
this.markDisplayed(input.completionId, outcome);
267267
}
268268

core/config/ConfigHandler.ts

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as fs from "node:fs";
2-
31
import { ConfigResult, FullSlug } from "@continuedev/config-yaml";
42

53
import {
@@ -17,9 +15,8 @@ import {
1715
} from "../index.js";
1816
import Ollama from "../llm/llms/Ollama.js";
1917
import { GlobalContext } from "../util/GlobalContext.js";
20-
import { getConfigJsonPath, getConfigYamlPath } from "../util/paths.js";
21-
import { localPathToUri } from "../util/pathToUri.js";
2218

19+
import { getAllAssistantFiles } from "./loadLocalAssistants.js";
2320
import {
2421
LOCAL_ONBOARDING_CHAT_MODEL,
2522
LOCAL_ONBOARDING_PROVIDER_TITLE,
@@ -53,6 +50,7 @@ export class ConfigHandler {
5350
private ideSettingsPromise: Promise<IdeSettings>,
5451
private readonly writeLog: (text: string) => Promise<void>,
5552
sessionInfoPromise: Promise<ControlPlaneSessionInfo | undefined>,
53+
private readonly didSelectOrganization?: (orgId: string | null) => void,
5654
) {
5755
this.ide = ide;
5856
this.ideSettingsPromise = ideSettingsPromise;
@@ -86,6 +84,25 @@ export class ConfigHandler {
8684
});
8785
}
8886

87+
/**
88+
* Users can define as many local assistants as they want in a `.continue/assistants` folder
89+
*/
90+
private async getLocalAssistantProfiles() {
91+
const assistantFiles = await getAllAssistantFiles(this.ide);
92+
const profiles = assistantFiles.map((assistant) => {
93+
return new LocalProfileLoader(
94+
this.ide,
95+
this.ideSettingsPromise,
96+
this.controlPlaneClient,
97+
this.writeLog,
98+
assistant,
99+
);
100+
});
101+
return profiles.map(
102+
(profile) => new ProfileLifecycleManager(profile, this.ide),
103+
);
104+
}
105+
89106
/**
90107
* Retrieves the titles of additional context providers that are of type "submenu".
91108
*
@@ -103,8 +120,9 @@ export class ConfigHandler {
103120
} catch (e) {
104121
// If this fails, make sure at least local profile is loaded
105122
console.error("Failed to fetch control plane profiles in init: ", e);
106-
await this.updateAvailableProfiles([this.localProfileManager]);
123+
await this.loadLocalProfilesOnly();
107124
}
125+
108126
try {
109127
const configResult = await this.loadConfig();
110128
this.notifyConfigListeners(configResult);
@@ -135,14 +153,12 @@ export class ConfigHandler {
135153

136154
async openConfigProfile(profileId?: string) {
137155
let openProfileId = profileId || this.selectedProfileId;
138-
if (openProfileId === "local") {
156+
const profile = this.profiles?.find(
157+
(p) => p.profileDescription.id === openProfileId,
158+
);
159+
if (profile?.profileDescription.profileType === "local") {
139160
const ideInfo = await this.ide.getIdeInfo();
140-
const configYamlPath = getConfigYamlPath(ideInfo.ideType);
141-
if (fs.existsSync(configYamlPath)) {
142-
await this.ide.openFile(localPathToUri(configYamlPath));
143-
} else {
144-
await this.ide.openFile(localPathToUri(getConfigJsonPath()));
145-
}
161+
await this.ide.openFile(profile.profileDescription.uri);
146162
} else {
147163
const env = await getControlPlaneEnv(this.ide.getIdeSettings());
148164
await this.ide.openUrl(`${env.APP_URL}${openProfileId}`);
@@ -161,35 +177,39 @@ export class ConfigHandler {
161177
let profiles: ProfileLifecycleManager[] | null = null;
162178
if (!userId) {
163179
// Not logged in
164-
profiles = [this.localProfileManager];
180+
const allLocalProfiles = await this.getAllLocalProfiles();
181+
profiles = [...allLocalProfiles];
165182
} else {
166183
// Logged in
167184
const assistants =
168185
await this.controlPlaneClient.listAssistants(selectedOrgId);
169186

170-
const hubProfiles = assistants.map((assistant) => {
171-
const profileLoader = new PlatformProfileLoader(
172-
{
173-
...assistant.configResult,
174-
config: assistant.configResult.config,
175-
},
176-
assistant.ownerSlug,
177-
assistant.packageSlug,
178-
assistant.iconUrl,
179-
assistant.configResult.config?.version ?? "latest",
180-
this.controlPlaneClient,
181-
this.ide,
182-
this.ideSettingsPromise,
183-
this.writeLog,
184-
this.reloadConfig.bind(this),
185-
);
186-
187-
return new ProfileLifecycleManager(profileLoader, this.ide);
188-
});
187+
const hubProfiles = await Promise.all(
188+
assistants.map(async (assistant) => {
189+
const profileLoader = await PlatformProfileLoader.create(
190+
{
191+
...assistant.configResult,
192+
config: assistant.configResult.config,
193+
},
194+
assistant.ownerSlug,
195+
assistant.packageSlug,
196+
assistant.iconUrl,
197+
assistant.configResult.config?.version ?? "latest",
198+
this.controlPlaneClient,
199+
this.ide,
200+
this.ideSettingsPromise,
201+
this.writeLog,
202+
this.reloadConfig.bind(this),
203+
);
204+
205+
return new ProfileLifecycleManager(profileLoader, this.ide);
206+
}),
207+
);
189208

190209
if (selectedOrgId === null) {
191210
// Personal
192-
profiles = [...hubProfiles, this.localProfileManager];
211+
const allLocalProfiles = await this.getAllLocalProfiles();
212+
profiles = [...hubProfiles, ...allLocalProfiles];
193213
} else {
194214
// Organization
195215
profiles = hubProfiles;
@@ -199,6 +219,16 @@ export class ConfigHandler {
199219
await this.updateAvailableProfiles(profiles);
200220
}
201221

222+
private async getAllLocalProfiles() {
223+
const localAssistantProfiles = await this.getLocalAssistantProfiles();
224+
return [this.localProfileManager, ...localAssistantProfiles];
225+
}
226+
227+
private async loadLocalProfilesOnly() {
228+
const allLocalProfiles = await this.getAllLocalProfiles();
229+
await this.updateAvailableProfiles(allLocalProfiles);
230+
}
231+
202232
private async updateAvailableProfiles(profiles: ProfileLifecycleManager[]) {
203233
this.profiles = profiles;
204234

@@ -278,7 +308,7 @@ export class ConfigHandler {
278308
} else {
279309
try {
280310
const workspaces = await this.controlPlaneClient.listWorkspaces();
281-
const profiles = [this.localProfileManager];
311+
const profiles = await this.getAllLocalProfiles();
282312
workspaces.forEach((workspace) => {
283313
const profileLoader = new ControlPlaneProfileLoader(
284314
workspace.id,
@@ -296,7 +326,7 @@ export class ConfigHandler {
296326
await this.updateAvailableProfiles(profiles);
297327
} catch (e: any) {
298328
console.error("Failed to load profiles: ", e);
299-
await this.updateAvailableProfiles([this.localProfileManager]);
329+
await this.loadLocalProfilesOnly();
300330
}
301331
}
302332
}
@@ -311,17 +341,22 @@ export class ConfigHandler {
311341
async getSelectedOrgId(): Promise<string | null> {
312342
const selectedOrgs =
313343
this.globalContext.get("lastSelectedOrgIdForWorkspace") ?? {};
314-
return selectedOrgs[await this.getWorkspaceId()] ?? null;
344+
const workspaceId = await this.getWorkspaceId();
345+
return selectedOrgs[workspaceId] ?? null;
315346
}
316347

317348
async setSelectedOrgId(orgId: string | null) {
318349
const selectedOrgs =
319350
this.globalContext.get("lastSelectedOrgIdForWorkspace") ?? {};
320351
selectedOrgs[await this.getWorkspaceId()] = orgId;
321352
this.globalContext.update("lastSelectedOrgIdForWorkspace", selectedOrgs);
353+
this.didSelectOrganization?.(orgId);
322354
}
323355

324356
async setSelectedProfile(profileId: string | null) {
357+
console.log(
358+
`Changing selected profile from ${this.selectedProfileId} to ${profileId}`,
359+
);
325360
this.selectedProfileId = profileId;
326361
const result = await this.loadConfig();
327362
this.notifyConfigListeners(result);
@@ -353,9 +388,20 @@ export class ConfigHandler {
353388
Promise.resolve(sessionInfo),
354389
this.ideSettingsPromise,
355390
);
391+
392+
// After login, default to the first org as the selected org
393+
try {
394+
const orgs = await this.controlPlaneClient.listOrganizations();
395+
if (orgs.length) {
396+
await this.setSelectedOrgId(orgs[0].id);
397+
}
398+
} catch (e) {
399+
console.error("Failed to fetch control plane profiles: ", e);
400+
}
401+
356402
this.fetchControlPlaneProfiles().catch(async (e) => {
357403
console.error("Failed to fetch control plane profiles: ", e);
358-
await this.updateAvailableProfiles([this.localProfileManager]);
404+
await this.loadLocalProfilesOnly();
359405
await this.reloadConfig();
360406
});
361407
}

core/config/ProfileLifecycleManager.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface ProfileDescription {
2121
id: string;
2222
iconUrl: string;
2323
errors: ConfigValidationError[] | undefined;
24+
uri: string;
2425
}
2526

2627
export interface OrganizationDescription {
@@ -77,14 +78,18 @@ export class ProfileLifecycleManager {
7778
// Set pending config promise
7879
this.pendingConfigPromise = new Promise(async (resolve, reject) => {
7980
let result: ConfigResult<ContinueConfig>;
81+
// This try catch is expected to catch high-level errors that aren't block-specific
82+
// Like invalid json, invalid yaml, file read errors, etc.
83+
// NOT block-specific loading errors
8084
try {
8185
result = await this.profileLoader.doLoadConfig();
82-
} catch (e: any) {
86+
} catch (e) {
87+
const message = e instanceof Error ? e.message : "Error loading config";
8388
result = {
8489
errors: [
8590
{
8691
fatal: true,
87-
message: e.message,
92+
message,
8893
},
8994
],
9095
config: undefined,
@@ -99,7 +104,6 @@ export class ProfileLifecycleManager {
99104
).concat(additionalContextProviders);
100105
}
101106

102-
this.savedConfigResult = result;
103107
resolve(result);
104108
});
105109

0 commit comments

Comments
 (0)