Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/config/markdown/loadMarkdownRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function loadMarkdownRules(ide: IDE): Promise<{
});
rules.push({
...rule,
source: "agent-file",
source: "agentFile",
sourceFile: agentFileUri,
alwaysApply: true,
});
Expand Down
2 changes: 1 addition & 1 deletion core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1852,7 +1852,7 @@ export type RuleSource =
| "colocated-markdown"
| "json-systemMessage"
| ".continuerules"
| "agent-file";
| "agentFile";

export interface RuleWithSource {
name?: string;
Expand Down
2 changes: 1 addition & 1 deletion core/llm/rules/rules-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function getRuleSourceDisplayName(rule: RuleWithSource): string {
return "Base System Plan Message";
case "model-options-chat":
return "Base System Chat Message";
case "agent-file":
case "agentFile":
if (rule.sourceFile) {
return getLastNPathParts(rule.sourceFile, 2);
} else {
Expand Down
2 changes: 1 addition & 1 deletion extensions/cli/src/__mocks__/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export const SERVICE_NAMES = {
CHAT_HISTORY: "chatHistory",
UPDATE: "update",
STORAGE_SYNC: "storageSync",
WORKFLOW: "workflow",
AGENT_FILE: "agentFile",
} as const;
4 changes: 2 additions & 2 deletions extensions/cli/src/commands/BaseCommandOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export interface BaseCommandOptions {
ask?: string[];
/** Array of tools to exclude from use (--exclude) */
exclude?: string[];
/** Workflow slug from the hub (--workflow) */
workflow?: string;
/** Agent file slug from the hub (--agent) */
agent?: string;
}

/**
Expand Down
14 changes: 7 additions & 7 deletions extensions/cli/src/commands/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { sentryService } from "../sentry.js";
import { initializeServices, services } from "../services/index.js";
import { serviceContainer } from "../services/ServiceContainer.js";
import {
AgentFileServiceState,
ModelServiceState,
SERVICE_NAMES,
WorkflowServiceState,
} from "../services/types.js";
import {
loadSession,
Expand Down Expand Up @@ -478,11 +478,11 @@ async function runHeadlessMode(
const { processAndCombinePrompts } = await import(
"../util/promptProcessor.js"
);
const workflowState = await serviceContainer.get<WorkflowServiceState>(
SERVICE_NAMES.WORKFLOW,
const agentFileState = await serviceContainer.get<AgentFileServiceState>(
SERVICE_NAMES.AGENT_FILE,
);
const initialPrompt =
`${workflowState?.workflowFile?.prompt ?? ""}\n\n${prompt ?? ""}`.trim() ||
`${agentFileState?.agentFile?.prompt ?? ""}\n\n${prompt ?? ""}`.trim() ||
undefined;
const initialUserInput = await processAndCombinePrompts(
options.prompt,
Expand Down Expand Up @@ -549,12 +549,12 @@ export async function chat(prompt?: string, options: ChatOptions = {}) {
toolPermissionOverrides: permissionOverrides,
});

const workflowState = await serviceContainer.get<WorkflowServiceState>(
SERVICE_NAMES.WORKFLOW,
const agentFileState = await serviceContainer.get<AgentFileServiceState>(
SERVICE_NAMES.AGENT_FILE,
);

const initialPrompt =
`${workflowState?.workflowFile?.prompt ?? ""}\n\n${prompt ?? ""}`.trim() ||
`${agentFileState?.agentFile?.prompt ?? ""}\n\n${prompt ?? ""}`.trim() ||
undefined;

// Start TUI with skipOnboarding since we already handled it
Expand Down
36 changes: 18 additions & 18 deletions extensions/cli/src/configEnhancer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ vi.mock("./hubLoader.js", () => ({
modelProcessor: {},
}));

// Mock the service container to provide empty workflow state
// Mock the service container to provide empty agent file state
vi.mock("./services/ServiceContainer.js", () => ({
serviceContainer: {
get: vi.fn(() =>
Promise.resolve({
workflowService: null,
workflowModelName: null,
workflowFile: null,
workflow: null,
agentFileService: null,
agentFileModelName: null,
agentFile: null,
slug: null,
}),
),
},
}));

vi.mock("./services/types.js", () => ({
SERVICE_NAMES: {
WORKFLOW: "workflow",
AGENT_FILE: "agentFile",
},
}));

Expand Down Expand Up @@ -288,41 +288,41 @@ describe("ConfigEnhancer", () => {
});
});

it("should handle workflow integration gracefully when no workflow", async () => {
// The mocked service container returns null workflow state
it("should handle agent file integration gracefully when no agent file", async () => {
// The mocked service container returns null agent file state
const options: BaseCommandOptions = {
rule: ["test-rule"],
};

const config = await enhancer.enhanceConfig(mockConfig, options);

// Should work normally when no workflow is active
// Should work normally when no agent file is active
expect(config.rules).toEqual(["test-rule"]);
});

it("should handle workflow integration when workflow is active", async () => {
// Mock service container to return active workflow
it("should handle agent file integration when agent file is active", async () => {
// Mock service container to return active agent file
const options: BaseCommandOptions = {
rule: ["user-rule"],
prompt: ["user-prompt"],
};

const config = await enhancer.enhanceConfig(mockConfig, options, {
workflowFile: {
name: "Test Workflow",
agentFile: {
name: "Test Agent",
prompt: "You are a test assistant",
rules: "Always be helpful",
model: "gpt-4",
tools: "bash,read",
},
slug: "owner/test-workflow",
workflowModelName: null,
workflowService: null,
slug: "owner/test-agent",
agentFileModelName: null,
agentFileService: null,
});

// Should have both workflow and user rules
// Should have both agent file and user rules
expect(config.rules).toHaveLength(2);
expect(config.rules?.[0]).toBe("Always be helpful"); // Workflow rule first
expect(config.rules?.[0]).toBe("Always be helpful"); // Agent file rule first
expect(config.rules?.[1]).toBe("user-rule"); // User rule second
});
});
43 changes: 23 additions & 20 deletions extensions/cli/src/configEnhancer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
AssistantUnrolled,
parseWorkflowTools,
parseAgentFileTools,
Rule,
} from "@continuedev/config-yaml";

Expand All @@ -12,24 +12,24 @@ import {
modelProcessor,
processRule,
} from "./hubLoader.js";
import { WorkflowServiceState } from "./services/types.js";
import { AgentFileServiceState } from "./services/types.js";
import { logger } from "./util/logger.js";

/**
* Enhances a configuration by injecting additional components from CLI flags
*/
export class ConfigEnhancer {
// added this for lint complexity rule
private async enhanceConfigFromWorkflow(
private async enhanceConfigFromAgentFile(
config: AssistantUnrolled,
_options: BaseCommandOptions | undefined,
workflowState?: WorkflowServiceState,
agentFileState?: AgentFileServiceState,
) {
const enhancedConfig = { ...config };
const options = { ..._options };

if (workflowState?.workflowFile) {
const { rules, model, tools, prompt } = workflowState?.workflowFile;
if (agentFileState?.agentFile) {
const { rules, model, tools, prompt } = agentFileState?.agentFile;
if (rules) {
options.rule = [
...rules
Expand All @@ -42,38 +42,41 @@ export class ConfigEnhancer {

if (tools) {
try {
const parsedTools = parseWorkflowTools(tools);
const parsedTools = parseAgentFileTools(tools);
if (parsedTools.mcpServers.length > 0) {
options.mcp = [...parsedTools.mcpServers, ...(options.mcp || [])];
}
} catch (e) {
logger.error("Failed to parse workflow tools", e);
logger.error("Failed to parse agent file tools", e);
}
}

// --model takes precedence over workflow model
// --model takes precedence over agent file model
if (model) {
try {
const workflowModel = await loadPackageFromHub(model, modelProcessor);
const agentFileModel = await loadPackageFromHub(
model,
modelProcessor,
);
enhancedConfig.models = [
workflowModel,
agentFileModel,
...(enhancedConfig.models ?? []),
];
workflowState?.workflowService?.setWorkflowModelName(
workflowModel.name,
agentFileState?.agentFileService?.setagentFileModelName(
agentFileModel.name,
);
} catch (e) {
logger.error("Failed to load workflow model", e);
logger.error("Failed to load agent model", e);
}
}

// Workflow prompt is included as a slash command, initial kickoff is handled elsewhere
// Agent file prompt is included as a slash command, initial kickoff is handled elsewhere
if (prompt) {
enhancedConfig.prompts = [
{
name: `Workflow prompt (${workflowState.workflowFile.name})`,
name: `Agent prompt (${agentFileState.agentFile.name})`,
prompt,
description: workflowState.workflowFile.description,
description: agentFileState.agentFile.description,
},
...(enhancedConfig.prompts ?? []),
];
Expand All @@ -87,12 +90,12 @@ export class ConfigEnhancer {
async enhanceConfig(
config: AssistantUnrolled,
_options?: BaseCommandOptions,
workflowState?: WorkflowServiceState,
agentFileState?: AgentFileServiceState,
): Promise<AssistantUnrolled> {
const enhanced = await this.enhanceConfigFromWorkflow(
const enhanced = await this.enhanceConfigFromAgentFile(
config,
_options,
workflowState,
agentFileState,
);
let { enhancedConfig } = enhanced;
const { options } = enhanced;
Expand Down
14 changes: 7 additions & 7 deletions extensions/cli/src/hubLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parseWorkflowFile, WorkflowFile } from "@continuedev/config-yaml";
import { AgentFile, parseAgentFile } from "@continuedev/config-yaml";
import JSZip from "jszip";

import { env } from "./env.js";
Expand All @@ -12,7 +12,7 @@ const HUB_SLUG_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
/**
* Hub package type definitions
*/
export type HubPackageType = "rule" | "mcp" | "model" | "prompt" | "workflow";
export type HubPackageType = "rule" | "mcp" | "model" | "prompt" | "agentFile";

/**
* Hub package processor interface
Expand Down Expand Up @@ -100,12 +100,12 @@ export const promptProcessor: HubPackageProcessor<string> = {
parseContent: (content: string) => content,
};

export const workflowProcessor: HubPackageProcessor<WorkflowFile> = {
type: "workflow",
export const agentFileProcessor: HubPackageProcessor<AgentFile> = {
type: "agentFile",
expectedFileExtensions: [".md"],
parseContent: (content: string) => parseWorkflowFile(content),
validateContent: (workflowFile: WorkflowFile) => {
return !!workflowFile.name;
parseContent: (content: string) => parseAgentFile(content),
validateContent: (agentFile: AgentFile) => {
return !!agentFile.name;
},
};

Expand Down
6 changes: 3 additions & 3 deletions extensions/cli/src/integration/rule-duplication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ vi.mock("../hubLoader.js", () => ({
modelProcessor: {},
}));

// Mock the service container to provide empty workflow state
// Mock the service container to provide empty agent file state
vi.mock("../services/ServiceContainer.js", () => ({
serviceContainer: {
get: vi.fn(() =>
Promise.resolve({
workflowFile: null,
agentFile: null,
slug: null,
}),
),
Expand All @@ -33,7 +33,7 @@ vi.mock("../services/ServiceContainer.js", () => ({

vi.mock("../services/types.js", () => ({
SERVICE_NAMES: {
WORKFLOW: "workflow",
AGENT_FILE: "agentFile",
},
}));

Expand Down
Loading
Loading