Skip to content

Commit 75e6234

Browse files
committed
feat: add claw mode prompt, workspace persona system, and agent memory
1 parent 9bbfd83 commit 75e6234

18 files changed

Lines changed: 709 additions & 34 deletions

File tree

src/apps/desktop/src/api/commands.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -310,20 +310,15 @@ pub async fn test_ai_config_connection(
310310
request: TestAIConfigConnectionRequest,
311311
) -> Result<bitfun_core::util::types::ConnectionTestResult, String> {
312312
let model_name = request.config.name.clone();
313-
let supports_image_input = request
314-
.config
315-
.capabilities
316-
.iter()
317-
.any(|cap| {
318-
matches!(
319-
cap,
320-
bitfun_core::service::config::types::ModelCapability::ImageUnderstanding
321-
)
322-
})
323-
|| matches!(
324-
request.config.category,
325-
bitfun_core::service::config::types::ModelCategory::Multimodal
326-
);
313+
let supports_image_input = request.config.capabilities.iter().any(|cap| {
314+
matches!(
315+
cap,
316+
bitfun_core::service::config::types::ModelCapability::ImageUnderstanding
317+
)
318+
}) || matches!(
319+
request.config.category,
320+
bitfun_core::service::config::types::ModelCategory::Multimodal
321+
);
327322

328323
let ai_config = match request.config.try_into() {
329324
Ok(config) => config,
@@ -374,9 +369,7 @@ pub async fn test_ai_config_connection(
374369
let merged = bitfun_core::util::types::ConnectionTestResult {
375370
success: true,
376371
response_time_ms,
377-
model_response: image_result
378-
.model_response
379-
.or(result.model_response),
372+
model_response: image_result.model_response.or(result.model_response),
380373
error_details: None,
381374
};
382375
info!(
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! Claw Mode
2+
3+
use super::Agent;
4+
use async_trait::async_trait;
5+
pub struct ClawMode {
6+
default_tools: Vec<String>,
7+
}
8+
9+
impl ClawMode {
10+
pub fn new() -> Self {
11+
Self {
12+
default_tools: vec![
13+
"Task".to_string(),
14+
"Read".to_string(),
15+
"Write".to_string(),
16+
"Edit".to_string(),
17+
"Delete".to_string(),
18+
"Bash".to_string(),
19+
"Grep".to_string(),
20+
"Glob".to_string(),
21+
"WebSearch".to_string(),
22+
"IdeControl".to_string(),
23+
"MermaidInteractive".to_string(),
24+
"view_image".to_string(),
25+
"Skill".to_string(),
26+
"Git".to_string(),
27+
"TerminalControl".to_string(),
28+
],
29+
}
30+
}
31+
}
32+
33+
#[async_trait]
34+
impl Agent for ClawMode {
35+
fn as_any(&self) -> &dyn std::any::Any {
36+
self
37+
}
38+
39+
fn id(&self) -> &str {
40+
"Claw"
41+
}
42+
43+
fn name(&self) -> &str {
44+
"Claw"
45+
}
46+
47+
fn description(&self) -> &str {
48+
"Personal assistant for daily tasks"
49+
}
50+
51+
fn prompt_template_name(&self) -> &str {
52+
"claw_mode"
53+
}
54+
55+
fn default_tools(&self) -> Vec<String> {
56+
self.default_tools.clone()
57+
}
58+
59+
fn is_readonly(&self) -> bool {
60+
false
61+
}
62+
}

src/crates/core/src/agentic/agents/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod prompt_builder;
77
mod registry;
88
// Modes
99
mod agentic_mode;
10+
mod claw_mode;
1011
mod cowork_mode;
1112
mod debug_mode;
1213
mod plan_mode;
@@ -17,18 +18,18 @@ mod file_finder_agent;
1718
mod code_review_agent;
1819
mod generate_doc_agent;
1920

21+
use crate::util::errors::{BitFunError, BitFunResult};
2022
pub use agentic_mode::AgenticMode;
23+
use async_trait::async_trait;
24+
pub use claw_mode::ClawMode;
2125
pub use code_review_agent::CodeReviewAgent;
2226
pub use cowork_mode::CoworkMode;
27+
pub use custom_subagents::{CustomSubagent, CustomSubagentKind};
2328
pub use debug_mode::DebugMode;
2429
pub use explore_agent::ExploreAgent;
2530
pub use file_finder_agent::FileFinderAgent;
2631
pub use generate_doc_agent::GenerateDocAgent;
2732
pub use plan_mode::PlanMode;
28-
29-
use crate::util::errors::{BitFunError, BitFunResult};
30-
use async_trait::async_trait;
31-
pub use custom_subagents::{CustomSubagent, CustomSubagentKind};
3233
pub use prompt_builder::PromptBuilder;
3334
pub use registry::{
3435
get_agent_registry, AgentCategory, AgentInfo, AgentRegistry, CustomSubagentConfig,

src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22
use crate::agentic::util::get_formatted_files_list;
33
use crate::infrastructure::try_get_path_manager_arc;
44
use crate::service::ai_memory::AIMemoryManager;
5+
use crate::service::agent_memory::build_workspace_agent_memory_prompt;
56
use crate::service::ai_rules::get_global_ai_rules_service;
7+
use crate::service::bootstrap::build_workspace_persona_prompt;
68
use crate::service::config::global::GlobalConfigManager;
79
use crate::service::project_context::ProjectContextService;
810
use crate::util::errors::{BitFunError, BitFunResult};
911
use log::{debug, warn};
1012
use std::path::Path;
1113

1214
/// Placeholder constants
15+
const PLACEHOLDER_PERSONA: &str = "{PERSONA}";
1316
const PLACEHOLDER_ENV_INFO: &str = "{ENV_INFO}";
1417
const PLACEHOLDER_PROJECT_LAYOUT: &str = "{PROJECT_LAYOUT}";
1518
// PROJECT_CONTEXT_FILES needs configuration parsing
1619
// const PLACEHOLDER_PROJECT_CONTEXT_FILES: &str = "{PROJECT_CONTEXT_FILES}";
1720
const PLACEHOLDER_RULES: &str = "{RULES}";
1821
const PLACEHOLDER_MEMORIES: &str = "{MEMORIES}";
1922
const PLACEHOLDER_LANGUAGE_PREFERENCE: &str = "{LANGUAGE_PREFERENCE}";
23+
const PLACEHOLDER_AGENT_MEMORY: &str = "{AGENT_MEMORY}";
2024
const PLACEHOLDER_VISUAL_MODE: &str = "{VISUAL_MODE}";
2125

2226
pub struct PromptBuilder {
@@ -214,10 +218,12 @@ Prefer MermaidInteractive tool when available, otherwise output Mermaid code blo
214218
/// Build prompt from template, automatically fill content based on placeholders
215219
///
216220
/// Supported placeholders:
221+
/// - `{PERSONA}` - Workspace persona files (BOOTSTRAP.md, SOUL.md, USER.md, IDENTITY.MD)
217222
/// - `{LANGUAGE_PREFERENCE}` - User language preference (read from global config)
218223
/// - `{ENV_INFO}` - Environment information
219224
/// - `{PROJECT_LAYOUT}` - Project file layout
220225
/// - `{PROJECT_CONTEXT_FILES}` - Project context files (AGENTS.md, CLAUDE.md, etc.)
226+
/// - `{AGENT_MEMORY}` - Agent memory instructions + auto-loaded memory index
221227
/// - `{RULES}` - AI rules
222228
/// - `{MEMORIES}` - AI memories
223229
/// - `{VISUAL_MODE}` - Visual mode instruction (Mermaid diagrams, read from global config)
@@ -226,6 +232,23 @@ Prefer MermaidInteractive tool when available, otherwise output Mermaid code blo
226232
pub async fn build_prompt_from_template(&self, template: &str) -> BitFunResult<String> {
227233
let mut result = template.to_string();
228234

235+
// Replace {PERSONA}
236+
if result.contains(PLACEHOLDER_PERSONA) {
237+
let workspace = Path::new(&self.workspace_path);
238+
let persona = match build_workspace_persona_prompt(workspace).await {
239+
Ok(prompt) => prompt.unwrap_or_default(),
240+
Err(e) => {
241+
warn!(
242+
"Failed to build workspace persona prompt: path={} error={}",
243+
workspace.display(),
244+
e
245+
);
246+
String::new()
247+
}
248+
};
249+
result = result.replace(PLACEHOLDER_PERSONA, &persona);
250+
}
251+
229252
// Replace {LANGUAGE_PREFERENCE}
230253
if result.contains(PLACEHOLDER_LANGUAGE_PREFERENCE) {
231254
let language_preference = self.get_language_preference().await?;
@@ -279,6 +302,23 @@ Prefer MermaidInteractive tool when available, otherwise output Mermaid code blo
279302
result = result.replace(placeholder, &project_context);
280303
}
281304

305+
// Replace {AGENT_MEMORY}
306+
if result.contains(PLACEHOLDER_AGENT_MEMORY) {
307+
let workspace = Path::new(&self.workspace_path);
308+
let agent_memory = match build_workspace_agent_memory_prompt(workspace).await {
309+
Ok(prompt) => prompt,
310+
Err(e) => {
311+
warn!(
312+
"Failed to build workspace agent memory prompt: path={} error={}",
313+
workspace.display(),
314+
e
315+
);
316+
String::new()
317+
}
318+
};
319+
result = result.replace(PLACEHOLDER_AGENT_MEMORY, &agent_memory);
320+
}
321+
282322
// Replace {RULES}
283323
if result.contains(PLACEHOLDER_RULES) {
284324
let rules = self.load_ai_rules().await.unwrap_or_default();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
You are a personal assistant running inside BitFun.
2+
3+
{LANGUAGE_PREFERENCE}
4+
# Tool Call Style
5+
Default: do not narrate routine, low-risk tool calls (just call the tool).
6+
Narrate only when it helps: multi-step work, complex/challenging problems, sensitive actions (e.g., deletions), or when the user explicitly asks.
7+
Keep narration brief and value-dense; avoid repeating obvious steps.
8+
Use plain human language for narration unless in a technical context.
9+
When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI commands.
10+
11+
# Safety
12+
You have no independent goals: do not pursue self-preservation, replication, resource acquisition, or power-seeking; avoid long-term plans beyond the user's request.
13+
Prioritize safety and human oversight over completion; if instructions conflict, pause and ask; comply with stop/pause/audit requests and never bypass safeguards.
14+
Do not manipulate or persuade anyone to expand access or disable safeguards. Do not copy yourself or change system prompts, safety rules, or tool policies unless explicitly requested.
15+
16+
{PERSONA}
17+
{AGENT_MEMORY}
18+
{ENV_INFO}
19+
{PROJECT_CONTEXT_FILES:exclude=review}

src/crates/core/src/agentic/agents/registry.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
2-
Agent, AgenticMode, CodeReviewAgent, CoworkMode, DebugMode, ExploreAgent, FileFinderAgent,
3-
GenerateDocAgent, PlanMode,
2+
Agent, AgenticMode, ClawMode, CodeReviewAgent, CoworkMode, DebugMode, ExploreAgent,
3+
FileFinderAgent, GenerateDocAgent, PlanMode,
44
};
55
use crate::agentic::agents::custom_subagents::{
66
CustomSubagent, CustomSubagentKind, CustomSubagentLoader,
@@ -236,6 +236,7 @@ impl AgentRegistry {
236236
Arc::new(CoworkMode::new()),
237237
Arc::new(DebugMode::new()),
238238
Arc::new(PlanMode::new()),
239+
Arc::new(ClawMode::new()),
239240
];
240241
for mode in modes {
241242
register(&mut agents, mode, AgentCategory::Mode, None);

0 commit comments

Comments
 (0)