-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Hermes architecture, Agentic UI, and CLI optimizations #1147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { FREEBUFF_MIMO_V25_MODEL_ID } from '@codebuff/common/constants/freebuff-models' | ||
|
|
||
| import { createBase3CliRoot } from './base3' | ||
|
|
||
| const definition = { | ||
| ...createBase3CliRoot({ | ||
| model: FREEBUFF_MIMO_V25_MODEL_ID, | ||
| isFreebuff: true, | ||
| }), | ||
| id: 'mcp-enabled', | ||
| displayName: 'Buffy with MCP', | ||
| mcpServers: { | ||
| filesystem: { | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-filesystem'], | ||
| }, | ||
| memory: { | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-memory'], | ||
| }, | ||
| 'sequential-thinking': { | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-sequential-thinking'], | ||
| }, | ||
| puppeteer: { | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-puppeteer'], | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| export default definition |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { | ||
| closeAllMCPClients, | ||
| getMCPClient, | ||
| getMCPClientId, | ||
| getMCPClientStatuses, | ||
| listMCPTools, | ||
| } from '@codebuff/common/mcp/client' | ||
|
|
||
| import { | ||
| getLoadedMCPServers, | ||
| initializeAgentRegistry, | ||
| } from '../utils/local-agent-registry' | ||
|
|
||
| import type { MCPConfig } from '@codebuff/common/types/mcp' | ||
|
|
||
| function describeConfig(config: MCPConfig): string { | ||
| if (config.type === 'stdio') return `stdio · ${config.command}` | ||
| try { | ||
| const url = new URL(config.url) | ||
| return `${config.type} · ${url.origin}${url.pathname}` | ||
| } catch { | ||
| return `${config.type} · endereço inválido` | ||
| } | ||
| } | ||
|
|
||
| export function formatMCPStatus(): string { | ||
| const servers = getLoadedMCPServers() | ||
| const statuses = getMCPClientStatuses() | ||
| if (!Object.keys(servers).length) { | ||
| return 'Nenhum servidor MCP configurado. Use .agents/mcp.json, .mcp.json ou .cursor/mcp.json.' | ||
| } | ||
|
|
||
| const readyIds = new Set( | ||
| statuses.filter((status) => status.state === 'ready').map((status) => status.id), | ||
| ) | ||
| return Object.entries(servers) | ||
| .map(([name, config]) => { | ||
| const active = readyIds.has(getMCPClientId(config)) | ||
| ? 'conectado' | ||
| : 'configurado' | ||
| return `${name} · ${describeConfig(config)} · ${active}` | ||
| }) | ||
| .join('\n') | ||
| } | ||
|
|
||
| export async function runMCPCommand(args: string): Promise<string> { | ||
| const trimmed = args.trim() | ||
| const [verb, name] = trimmed ? trimmed.split(/\s+/, 2) : ['status'] | ||
| if (verb === 'status' || verb === 'list') return formatMCPStatus() | ||
|
|
||
| if (verb === 'reload') { | ||
| await closeAllMCPClients() | ||
| await initializeAgentRegistry() | ||
| return `MCP recarregado.\n${formatMCPStatus()}` | ||
| } | ||
|
|
||
| if (verb === 'test') { | ||
| if (!name) return 'Uso: /mcp test <servidor>' | ||
| const config = getLoadedMCPServers()[name] | ||
| if (!config) return `Servidor MCP não encontrado: ${name}` | ||
| try { | ||
| const clientId = await getMCPClient(config) | ||
| const result = await listMCPTools(clientId) | ||
| return `${name} conectado · ${result.tools.length} ferramentas disponíveis` | ||
| } catch (error) { | ||
| return `${name} falhou: ${error instanceof Error ? error.message : String(error)}` | ||
| } | ||
| } | ||
|
|
||
| return 'Uso: /mcp [status|list|reload|test <servidor>]' | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { getProjectRoot } from '../project-files' | ||
| import { | ||
| buildMissionPrompt, | ||
| cancelMission, | ||
| completeMission, | ||
| createMission, | ||
| formatMissionStatus, | ||
| loadMission, | ||
| } from '../missions/mission-store' | ||
|
|
||
| function root(): string { | ||
| return getProjectRoot() || process.cwd() | ||
| } | ||
|
|
||
| export type MissionCommandResult = | ||
| | { kind: 'message'; message: string } | ||
| | { kind: 'start'; prompt: string } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For a bare Useful? React with 👍 / 👎. |
||
|
|
||
| export function runMissionCommand(args: string): MissionCommandResult { | ||
| const trimmed = args.trim() | ||
| let [verb, ...rest] = trimmed ? trimmed.split(/\s+/) : ['status'] | ||
|
|
||
| if (verb.endsWith(',')) { | ||
| verb = verb.slice(0, -1) | ||
| } | ||
|
|
||
| const value = rest.join(' ').trim() | ||
|
|
||
| if (verb === 'status') { | ||
| return { kind: 'message', message: formatMissionStatus(loadMission(root())) } | ||
| } | ||
| if (verb === 'start') { | ||
| if (!value) { | ||
| return { kind: 'message', message: 'Uso: /mission start <objetivo>' } | ||
| } | ||
| const mission = createMission(root(), value) | ||
| return { kind: 'start', prompt: buildMissionPrompt(mission) } | ||
| } | ||
| if (verb === 'complete') { | ||
| const evidence = value ? value.split('|').map((item) => item.trim()) : [] | ||
| try { | ||
| const mission = completeMission(root(), evidence) | ||
| return { kind: 'message', message: formatMissionStatus(mission) } | ||
| } catch (error) { | ||
| return { | ||
| kind: 'message', | ||
| message: error instanceof Error ? error.message : String(error), | ||
| } | ||
| } | ||
| } | ||
| if (verb === 'cancel') { | ||
| try { | ||
| const mission = cancelMission(root()) | ||
| return { kind: 'message', message: formatMissionStatus(mission) } | ||
| } catch (error) { | ||
| return { | ||
| kind: 'message', | ||
| message: error instanceof Error ? error.message : String(error), | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| kind: 'message', | ||
| message: 'Uso: /mission [status|start <objetivo>|complete [evidência]|cancel]', | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a bare
/mcp,args.trim()is empty and splitting it still yields[''], soverbbecomes''instead of thestatusdefault and the command returns usage text. Since this command is meant to inspect MCP status by default, handle the empty string before splitting.Useful? React with 👍 / 👎.