Skip to content
Open
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
14 changes: 13 additions & 1 deletion crates/executors/default_profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,18 @@
}
}
},
"CODEBUDDY": {
"DEFAULT": {
"CODEBUDDY": {
"yolo": true
}
},
"APPROVALS": {
"CODEBUDDY": {
"yolo": false
}
}
},
"DROID": {
"DEFAULT": {
"DROID": {
Expand Down Expand Up @@ -332,4 +344,4 @@
}
}
}
}
}
132 changes: 132 additions & 0 deletions crates/executors/src/executors/codebuddy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::{path::Path, sync::Arc};

use async_trait::async_trait;
use derivative::Derivative;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use workspace_utils::msg_store::MsgStore;

use crate::{
approvals::ExecutorApprovalService,
command::{CmdOverrides, CommandBuilder, apply_overrides},
env::ExecutionEnv,
executors::{
AppendPrompt, AvailabilityInfo, ExecutorError, SpawnedChild, StandardCodingAgentExecutor,
acp::AcpAgentHarness,
},
};

#[derive(Derivative, Clone, Serialize, Deserialize, TS, JsonSchema)]
#[derivative(Debug, PartialEq)]
pub struct CodeBuddy {
#[serde(default)]
pub append_prompt: AppendPrompt,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub yolo: Option<bool>,
#[serde(flatten)]
pub cmd: CmdOverrides,
#[serde(skip)]
#[ts(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub approvals: Option<Arc<dyn ExecutorApprovalService>>,
}

impl CodeBuddy {
fn build_command_builder(&self) -> CommandBuilder {
let mut builder = CommandBuilder::new("npx -y @tencent-ai/codebuddy-code");

if self.yolo.unwrap_or(false) {
builder = builder.extend_params(["-y"]);
}
builder = builder.extend_params(["--acp"]);
apply_overrides(builder, &self.cmd)
}
}

#[async_trait]
impl StandardCodingAgentExecutor for CodeBuddy {
fn use_approvals(&mut self, approvals: Arc<dyn ExecutorApprovalService>) {
self.approvals = Some(approvals);
}

async fn spawn(
&self,
current_dir: &Path,
prompt: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError> {
let codebuddy_command = self.build_command_builder().build_initial()?;
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let harness = AcpAgentHarness::with_session_namespace("codebuddy_sessions");
let approvals = if self.yolo.unwrap_or(false) {
None
} else {
self.approvals.clone()
};
harness
.spawn_with_command(
current_dir,
combined_prompt,
codebuddy_command,
env,
&self.cmd,
approvals,
)
.await
}

async fn spawn_follow_up(
&self,
current_dir: &Path,
prompt: &str,
session_id: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError> {
let codebuddy_command = self.build_command_builder().build_follow_up(&[])?;
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let harness = AcpAgentHarness::with_session_namespace("codebuddy_sessions");
let approvals = if self.yolo.unwrap_or(false) {
None
} else {
self.approvals.clone()
};
harness
.spawn_follow_up_with_command(
current_dir,
combined_prompt,
session_id,
codebuddy_command,
env,
&self.cmd,
approvals,
)
.await
}

fn normalize_logs(&self, msg_store: Arc<MsgStore>, worktree_path: &Path) {
crate::executors::acp::normalize_logs(msg_store, worktree_path);
}

// MCP configuration methods
fn default_mcp_config_path(&self) -> Option<std::path::PathBuf> {
dirs::home_dir().map(|home| home.join(".codebuddy").join("settings.json"))
}

fn get_availability_info(&self) -> AvailabilityInfo {
let mcp_config_found = self
.default_mcp_config_path()
.map(|p| p.exists())
.unwrap_or(false);

let installation_indicator_found = dirs::home_dir()
.map(|home| home.join(".codebuddy").join("installation_id").exists())
.unwrap_or(false);

if mcp_config_found || installation_indicator_found {
AvailabilityInfo::InstallationFound
} else {
AvailabilityInfo::NotFound
}
}
}
12 changes: 9 additions & 3 deletions crates/executors/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ use crate::{
command::CommandBuildError,
env::ExecutionEnv,
executors::{
amp::Amp, claude::ClaudeCode, codex::Codex, copilot::Copilot, cursor::CursorAgent,
droid::Droid, gemini::Gemini, opencode::Opencode, qwen::QwenCode,
amp::Amp, claude::ClaudeCode, codebuddy::CodeBuddy, codex::Codex, copilot::Copilot,
cursor::CursorAgent, droid::Droid, gemini::Gemini, opencode::Opencode, qwen::QwenCode,
},
mcp_config::McpConfig,
};

pub mod acp;
pub mod amp;
pub mod claude;
pub mod codebuddy;
pub mod codex;
pub mod copilot;
pub mod cursor;
Expand Down Expand Up @@ -104,6 +105,10 @@ pub enum CodingAgent {
QwenCode,
Copilot,
Droid,
#[serde(rename = "CODEBUDDY", alias = "CODE_BUDDY")]
#[strum_discriminants(strum(serialize = "CODEBUDDY"))]
#[strum_discriminants(serde(rename = "CODEBUDDY", alias = "CODE_BUDDY"))]
CodeBuddy,
#[cfg(feature = "qa-mode")]
QaMock(QaMockExecutor),
}
Expand Down Expand Up @@ -166,7 +171,8 @@ impl CodingAgent {
| Self::Gemini(_)
| Self::QwenCode(_)
| Self::Droid(_)
| Self::Opencode(_) => vec![BaseAgentCapability::SessionFork],
| Self::Opencode(_)
| Self::CodeBuddy(_) => vec![BaseAgentCapability::SessionFork],
Self::Codex(_) => vec![
BaseAgentCapability::SessionFork,
BaseAgentCapability::SetupHelper,
Expand Down
2 changes: 1 addition & 1 deletion crates/executors/src/mcp_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl CodingAgent {

let adapter = match self {
CodingAgent::ClaudeCode(_) | CodingAgent::Amp(_) | CodingAgent::Droid(_) => Passthrough,
CodingAgent::QwenCode(_) | CodingAgent::Gemini(_) => Gemini,
CodingAgent::QwenCode(_) | CodingAgent::Gemini(_) | CodingAgent::CodeBuddy(_) => Gemini,
CodingAgent::CursorAgent(_) => Cursor,
CodingAgent::Codex(_) => Codex,
CodingAgent::Opencode(_) => Opencode,
Expand Down
5 changes: 5 additions & 0 deletions crates/server/src/bin/generate_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ fn generate_types_content() -> String {
executors::executors::droid::Droid::decl(),
executors::executors::droid::Autonomy::decl(),
executors::executors::droid::ReasoningEffortLevel::decl(),
executors::executors::codebuddy::CodeBuddy::decl(),
executors::executors::AppendPrompt::decl(),
executors::actions::coding_agent_initial::CodingAgentInitialRequest::decl(),
executors::actions::coding_agent_follow_up::CodingAgentFollowUpRequest::decl(),
Expand Down Expand Up @@ -300,6 +301,10 @@ fn generate_schemas() -> Result<HashMap<&'static str, String>, serde_json::Error
"droid",
generate_json_schema::<executors::executors::droid::Droid>()?,
),
(
"codebuddy",
generate_json_schema::<executors::executors::codebuddy::CodeBuddy>()?,
),
]);
println!(
"✅ JSON schemas generated. {} schemas created.",
Expand Down
31 changes: 31 additions & 0 deletions docs/agents/codebuddy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: "CodeBuddy Code"
description: "Set up CodeBuddy Code agent"
icon: https://www.vibekanban.com/images/logos/codebuddy-logo.png#"
---

<Steps>
<Step title="Install CodeBuddy Code globally">
```bash
npm install -g @tencent-ai/codebuddy-code
```
</Step>

<Step title="Run CodeBuddy Code with ACP">
```bash
codebuddy --acp
```

Follow the login instructions and complete the authentication flow as prompted.
</Step>

<Step title="Start Vibe Kanban">
Once authenticated, launch Vibe Kanban:

```bash
npx vibe-kanban
```

You can now select CodeBuddy when creating task attempts.
</Step>
</Steps>
3 changes: 2 additions & 1 deletion docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"agents/opencode",
"agents/droid",
"agents/ccr",
"agents/qwen-code"
"agents/qwen-code",
"agents/codebuddy"
]
}
]
Expand Down
4 changes: 4 additions & 0 deletions docs/supported-coding-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ Claude Code Router - orchestrate multiple models
<Card title="Qwen Code" icon="https://www.vibekanban.com/images/logos/qwen-logo.png#" href="/agents/qwen-code">
Qwen Code CLI
</Card>

<Card title="CodeBuddy Code" icon="https://www.vibekanban.com/images/logos/codebuddy-logo.png#" href="/agents/codebuddy">
Tencent CodeBuddy Code
</Card>
</CardGroup>
52 changes: 52 additions & 0 deletions shared/schemas/codebuddy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"append_prompt": {
"title": "Append Prompt",
"description": "Extra text appended to the prompt",
"type": [
"string",
"null"
],
"format": "textarea",
"default": null
},
"yolo": {
"type": [
"boolean",
"null"
]
},
"base_command_override": {
"title": "Base Command Override",
"description": "Override the base command with a custom command",
"type": [
"string",
"null"
]
},
"additional_params": {
"title": "Additional Parameters",
"description": "Additional parameters to append to the base command",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"
}
8 changes: 5 additions & 3 deletions shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,9 @@ working_dir: string | null, };

export type ScriptRequestLanguage = "Bash";

export enum BaseCodingAgent { CLAUDE_CODE = "CLAUDE_CODE", AMP = "AMP", GEMINI = "GEMINI", CODEX = "CODEX", OPENCODE = "OPENCODE", CURSOR_AGENT = "CURSOR_AGENT", QWEN_CODE = "QWEN_CODE", COPILOT = "COPILOT", DROID = "DROID" }
export enum BaseCodingAgent { CLAUDE_CODE = "CLAUDE_CODE", AMP = "AMP", GEMINI = "GEMINI", CODEX = "CODEX", OPENCODE = "OPENCODE", CURSOR_AGENT = "CURSOR_AGENT", QWEN_CODE = "QWEN_CODE", COPILOT = "COPILOT", DROID = "DROID", CODEBUDDY = "CODEBUDDY" }

export type CodingAgent = { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid };
export type CodingAgent = { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } | { "CODEBUDDY": CodeBuddy };

export type AvailabilityInfo = { "type": "LOGIN_DETECTED", last_auth_timestamp: bigint, } | { "type": "INSTALLATION_FOUND" } | { "type": "NOT_FOUND" };

Expand All @@ -457,7 +457,7 @@ executor: BaseCodingAgent,
*/
variant: string | null, };

export type ExecutorConfig = { [key in string]?: { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } };
export type ExecutorConfig = { [key in string]?: { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } | { "CODEBUDDY": CodeBuddy } };

export type ExecutorConfigs = { executors: { [key in BaseCodingAgent]?: ExecutorConfig }, };

Expand Down Expand Up @@ -499,6 +499,8 @@ export type Autonomy = "normal" | "low" | "medium" | "high" | "skip-permissions-

export type DroidReasoningEffort = "none" | "dynamic" | "off" | "low" | "medium" | "high";

export type CodeBuddy = { append_prompt: AppendPrompt, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };

export type AppendPrompt = string | null;

export type CodingAgentInitialRequest = { prompt: string,
Expand Down