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
1 change: 1 addition & 0 deletions src/apps/desktop/src/computer_use/ui_locate_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ mod tests {
}

/// Whether an element's global bounds fall within any visible display.
#[allow(dead_code)]
pub fn is_element_on_screen(gx: f64, gy: f64, width: f64, height: f64) -> bool {
// Element must have reasonable size (not a giant container)
if width > 3000.0 || height > 2000.0 {
Expand Down
3 changes: 3 additions & 0 deletions src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ pub async fn run() {
.level_for("ignore", log::LevelFilter::Off)
.level_for("ignore::walk", log::LevelFilter::Off)
.level_for("globset", log::LevelFilter::Off)
.level_for("tracing", log::LevelFilter::Off)
.level_for("opentelemetry_sdk", log::LevelFilter::Off)
.level_for("opentelemetry-otlp", log::LevelFilter::Off)
.level_for("hyper_util", log::LevelFilter::Info)
.level_for("h2", log::LevelFilter::Info)
.level_for("portable_pty", log::LevelFilter::Info)
Expand Down
53 changes: 53 additions & 0 deletions src/crates/core/src/agentic/agents/init_agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use super::Agent;
use async_trait::async_trait;

pub struct InitAgent {
default_tools: Vec<String>,
}

impl InitAgent {
pub fn new() -> Self {
Self {
default_tools: vec![
"LS".to_string(),
"Read".to_string(),
"Grep".to_string(),
"Glob".to_string(),
"Write".to_string(),
"Edit".to_string(),
"Bash".to_string(),
],
}
}
}

#[async_trait]
impl Agent for InitAgent {
fn as_any(&self) -> &dyn std::any::Any {
self
}

fn id(&self) -> &str {
"Init"
}

fn name(&self) -> &str {
"Init"
}

fn description(&self) -> &str {
"Agent for /init command"
}

fn prompt_template_name(&self, _model_name: Option<&str>) -> &str {
"init_agent"
}

fn default_tools(&self) -> Vec<String> {
self.default_tools.clone()
}

fn is_readonly(&self) -> bool {
false
}
}
2 changes: 2 additions & 0 deletions src/crates/core/src/agentic/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod file_finder_agent;
// Hidden agents
mod code_review_agent;
mod generate_doc_agent;
mod init_agent;

use crate::util::errors::{BitFunError, BitFunResult};
pub use agentic_mode::AgenticMode;
Expand All @@ -29,6 +30,7 @@ pub use debug_mode::DebugMode;
pub use explore_agent::ExploreAgent;
pub use file_finder_agent::FileFinderAgent;
pub use generate_doc_agent::GenerateDocAgent;
pub use init_agent::InitAgent;
pub use plan_mode::PlanMode;
pub use prompt_builder::{PromptBuilder, PromptBuilderContext, RemoteExecutionHints};
pub use registry::{
Expand Down
18 changes: 18 additions & 0 deletions src/crates/core/src/agentic/agents/prompts/init_agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Please analyze this codebase and generate the content of an AGENTS.md file, which will be given to future instances of coding agents to operate in this repository.

What to add:
1. Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.
2. High-level code architecture and structure so that future instances can be productive more quickly. Focus on the "big picture" architecture that requires reading multiple files to understand.

Usage notes:
- "AGENTS.md", "CLAUDE.md", and ".github/copilot-instructions.md" serves the same purpose. If these files already exist, suggest improvements to them.
- When you make the initial AGENTS.md, do not repeat yourself and do not include obvious instructions like "Provide helpful error messages to users", "Write unit tests for all new utilities", "Never include sensitive information (API keys, tokens) in code or commits".
- Avoid listing every component or file structure that can be easily discovered.
- Don't include generic development practices.
- If there are Cursor rules (in .cursor/rules/ or .cursorrules), make sure to include the important parts.
- If there is a README.md, make sure to include the important parts.
- Do not make up information such as "Common Development Tasks", "Tips for Development", "Support and Documentation" unless this is expressly included in other files that you read.

{LANGUAGE_PREFERENCE}
{ENV_INFO}
{PROJECT_LAYOUT}
3 changes: 2 additions & 1 deletion src/crates/core/src/agentic/agents/registry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
Agent, AgenticMode, ClawMode, CodeReviewAgent, CoworkMode, DebugMode, ExploreAgent,
FileFinderAgent, GenerateDocAgent, PlanMode,
FileFinderAgent, GenerateDocAgent, InitAgent, PlanMode,
};
use crate::agentic::agents::custom_subagents::{
CustomSubagent, CustomSubagentKind, CustomSubagentLoader,
Expand Down Expand Up @@ -271,6 +271,7 @@ impl AgentRegistry {
let hidden_subagents: Vec<Arc<dyn Agent>> = vec![
Arc::new(CodeReviewAgent::new()),
Arc::new(GenerateDocAgent::new()),
Arc::new(InitAgent::new()),
];
for hidden_agent in hidden_subagents {
register(&mut agents, hidden_agent, AgentCategory::Hidden, None);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Folder, FolderOpen, MoreHorizontal, GitBranch, FolderSearch, Plus, ChevronDown, Trash2, RotateCcw, Copy } from 'lucide-react';
import { Folder, FolderOpen, MoreHorizontal, GitBranch, FolderSearch, Plus, ChevronDown, Trash2, RotateCcw, Copy, FileText } from 'lucide-react';
import { ConfirmDialog, Tooltip } from '@/component-library';
import { useI18n } from '@/infrastructure/i18n';
import { i18nService } from '@/infrastructure/i18n';
import { useWorkspaceContext } from '@/infrastructure/contexts/WorkspaceContext';
import {
createWorktreeWorkspace,
Expand Down Expand Up @@ -285,6 +286,41 @@ const WorkspaceItem: React.FC<WorkspaceItemProps> = ({
void handleCreateSession('Cowork');
}, [handleCreateSession]);

const handleCreateInitSession = useCallback(async () => {
setMenuOpen(false);

try {
const sessionId = await flowChatManager.createChatSession(
{
workspacePath: workspace.rootPath,
...(isRemoteWorkspace(workspace) && workspace.connectionId
? { remoteConnectionId: workspace.connectionId }
: {}),
...(isRemoteWorkspace(workspace) && workspace.sshHost
? { remoteSshHost: workspace.sshHost }
: {}),
},
'Init'
);

await openMainSession(sessionId, {
workspaceId: workspace.id,
activateWorkspace: setActiveWorkspace,
});

const initPrompt = i18nService.t('flow-chat:chatInput.initPrompt', {
defaultValue: 'Please generate or update AGENTS.md so it matches the current project. Write it in English and keep the English version complete.',
});

await flowChatManager.sendMessage(initPrompt, sessionId, initPrompt, 'Init');
} catch (error) {
notificationService.error(
error instanceof Error ? error.message : t('nav.workspaces.initSessionFailed'),
{ duration: 4000 }
);
}
}, [setActiveWorkspace, t, workspace]);

const handleCreateWorktree = useCallback(async (result: BranchSelectResult) => {
try {
const created = await createWorktreeWorkspace({
Expand Down Expand Up @@ -609,6 +645,10 @@ const WorkspaceItem: React.FC<WorkspaceItemProps> = ({
<Plus size={13} />
<span className="bitfun-nav-panel__workspace-item-menu-label">{t('nav.sessions.newCoworkSessionShort')}</span>
</button>
<button type="button" className="bitfun-nav-panel__workspace-item-menu-item" onClick={() => { void handleCreateInitSession(); }}>
<FileText size={13} />
<span className="bitfun-nav-panel__workspace-item-menu-label">{t('nav.workspaces.actions.initAgents')}</span>
</button>
{isLinkedWorktree ? (
<button
type="button"
Expand Down
90 changes: 90 additions & 0 deletions src/web-ui/src/flow_chat/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { useMessageSender } from '../hooks/useMessageSender';
import { useChatInputState } from '../store/chatInputStateStore';
import { useInputHistoryStore } from '../store/inputHistoryStore';
import { startBtwThread } from '../services/BtwThreadService';
import { FlowChatManager } from '../services/FlowChatManager';
import { createLogger } from '@/shared/utils/logger';
import { Tooltip, IconButton } from '@/component-library';
import { useAgentCanvasStore } from '@/app/components/panels/content-canvas/stores';
Expand Down Expand Up @@ -950,6 +951,74 @@ export const ChatInput: React.FC<ChatInputProps> = ({
setQueuedInput,
t,
]);

const submitInitFromInput = useCallback(async () => {
if (!effectiveTargetSessionId || !effectiveTargetSession) {
notificationService.error(
t('chatInput.initNoSession', { defaultValue: 'No active session for /init' })
);
return;
}

if (derivedState?.isProcessing) {
notificationService.warning(
t('chatInput.initBusy', {
defaultValue: 'Wait until the session is idle before using /init.',
})
);
return;
}

const message = inputState.value.trim();
if (!/^\/init\s*$/i.test(message)) {
notificationService.warning(
t('chatInput.initUsage', { defaultValue: 'Use /init without extra arguments.' })
);
return;
}

const initInstruction = t('chatInput.initPrompt', {
defaultValue: 'Please generate or update AGENTS.md so it matches the current project. Write it in English and keep the English version complete.',
});

dispatchInput({ type: 'CLEAR_VALUE' });
setQueuedInput(null);
setSlashCommandState({ isActive: false, kind: 'modes', query: '', selectedIndex: 0 });

try {
const flowChatManager = FlowChatManager.getInstance();
await flowChatManager.sendMessage(
initInstruction,
effectiveTargetSessionId,
initInstruction,
'Init'
);
onSendMessage?.(initInstruction);
dispatchInput({ type: 'DEACTIVATE' });
} catch (error) {
log.error('Failed to trigger /init', {
error,
sessionId: effectiveTargetSessionId,
});
dispatchInput({ type: 'ACTIVATE' });
dispatchInput({ type: 'SET_VALUE', payload: message });
notificationService.error(
error instanceof Error ? error.message : t('error.unknown'),
{
title: t('chatInput.initFailed', { defaultValue: 'Session init failed' }),
duration: 5000,
}
);
}
}, [
derivedState?.isProcessing,
effectiveTargetSession,
effectiveTargetSessionId,
inputState.value,
onSendMessage,
setQueuedInput,
t,
]);

const handleSendOrCancel = useCallback(async () => {
if (!derivedState) return;
Expand Down Expand Up @@ -986,12 +1055,24 @@ export const ChatInput: React.FC<ChatInputProps> = ({
return;
}

if (/^\/init\s*$/i.test(message)) {
await submitInitFromInput();
return;
}

if (message.toLowerCase().startsWith('/compact')) {
notificationService.warning(
t('chatInput.compactUsage', { defaultValue: 'Use /compact without extra arguments.' })
);
return;
}

if (message.toLowerCase().startsWith('/init')) {
notificationService.warning(
t('chatInput.initUsage', { defaultValue: 'Use /init without extra arguments.' })
);
return;
}

// Add to history before clearing (session-scoped)
if (effectiveTargetSessionId) {
Expand Down Expand Up @@ -1046,6 +1127,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
setQueuedInput,
submitBtwFromInput,
submitCompactFromInput,
submitInitFromInput,
t,
]);

Expand Down Expand Up @@ -1124,6 +1206,12 @@ export const ChatInput: React.FC<ChatInputProps> = ({
command: '/compact',
label: t('chatInput.compactAction', { defaultValue: 'Compact session' }),
},
{
kind: 'action',
id: 'init',
command: '/init',
label: t('chatInput.initAction', { defaultValue: 'Generate AGENTS.md' }),
},
];

const q = (slashCommandState.query || '').trim().toLowerCase();
Expand Down Expand Up @@ -1179,6 +1267,8 @@ export const ChatInput: React.FC<ChatInputProps> = ({
}
} else if (actionId === 'compact') {
next = '/compact';
} else if (actionId === 'init') {
next = '/init';
} else {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions src/web-ui/src/locales/en-US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@
"revealFailed": "Failed to reveal workspace folder",
"copyPathFailed": "Failed to copy path",
"createSessionFailed": "Failed to create session",
"initSessionFailed": "Failed to start AGENTS.md initialization session",
"worktreeCreated": "Worktree created",
"worktreeCreatedAndOpened": "Worktree created and opened",
"worktreeCreateFailed": "Failed to create worktree: {{error}}",
Expand All @@ -299,6 +300,7 @@
"newAssistant": "New personal assistant",
"deleteAssistant": "Delete personal assistant",
"resetWorkspace": "Reset workspace",
"initAgents": "Initialize AGENTS.md",
"newWorktree": "New worktree",
"deleteWorktree": "Delete worktree",
"copyPath": "Copy path",
Expand Down
6 changes: 6 additions & 0 deletions src/web-ui/src/locales/en-US/flow-chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@
"compactBusy": "Wait until the session is idle before using /compact.",
"compactUsage": "Use /compact without extra arguments.",
"compactFailed": "Session compaction failed",
"initAction": "Generate AGENTS.md",
"initNoSession": "No active session for /init",
"initBusy": "Wait until the session is idle before using /init.",
"initUsage": "Use /init without extra arguments.",
"initFailed": "AGENTS.md initialization failed",
"initPrompt": "Please generate or update AGENTS.md based on the current project content",
"currentMode": "Current mode: {{mode}}",
"noMatchingMode": "No matching mode",
"noMatchingCommand": "No matching command",
Expand Down
2 changes: 2 additions & 0 deletions src/web-ui/src/locales/zh-CN/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@
"revealFailed": "打开工作区目录失败",
"copyPathFailed": "复制路径失败",
"createSessionFailed": "新建会话失败",
"initSessionFailed": "初始化 AGENTS.md 会话失败",
"worktreeCreated": "已创建 worktree",
"worktreeCreatedAndOpened": "已创建并打开 worktree",
"worktreeCreateFailed": "创建 worktree 失败:{{error}}",
Expand All @@ -299,6 +300,7 @@
"newAssistant": "新建个人助理",
"deleteAssistant": "删除个人助理",
"resetWorkspace": "重置工作区",
"initAgents": "初始化 AGENTS.md",
"newWorktree": "新建 worktree",
"deleteWorktree": "删除 worktree",
"copyPath": "复制路径",
Expand Down
6 changes: 6 additions & 0 deletions src/web-ui/src/locales/zh-CN/flow-chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@
"compactBusy": "请等待当前会话空闲后再使用 /compact。",
"compactUsage": "请直接使用 /compact,不要附加额外参数。",
"compactFailed": "会话压缩失败",
"initAction": "生成 AGENTS.md",
"initNoSession": "当前没有可用于 /init 的会话",
"initBusy": "请等待当前会话空闲后再使用 /init。",
"initUsage": "请直接使用 /init,不要附加额外参数。",
"initFailed": "初始化 AGENTS.md 失败",
"initPrompt": "请根据当前项目内容生成或更新 AGENTS.md",
"currentMode": "当前模式: {{mode}}",
"noMatchingMode": "没有匹配的模式",
"noMatchingCommand": "没有匹配的命令",
Expand Down
Loading