Files
zeekay 1725d71492 feat(tools): bidirectional HIP-0300 action sync across Python/TS/Rust
Sync all tool actions across all three language implementations:
- code: +8 actions (search_symbol, outline, metrics, exports, types, hierarchy, rename, grep_replace)
- fetch: +2 actions (request, open)
- think: restructured to 12 action-based handlers
- tasks: +12 actions (stats, search, batch, archive, move, prioritize, assign, subtasks, notes, export, import, delete)
- plan: +13 actions (create, show, list, next, archive, add_step, remove_step, estimate, visualize, clone, cancel, notes, progress)
- mode: +3 actions (switch alias, list_presets, select_preset)
- memory: +10 actions (search, stats, clear, export, import, merge, tag, untag, namespaces, history)
- fs: +1 action (mv)
- git: +21 actions (blame, show, stash, tag, remote, merge, rebase, cherry_pick, reset, clean, init, clone, fetch, pull, push, config, worktree, reflog, shortlog, rev_parse, describe, bisect)
2026-03-10 21:57:50 -07:00

366 lines
12 KiB
Python

"""Tool for managing development modes with programmer personalities."""
from typing import Optional, override
from mcp.server import FastMCP
from mcp.server.fastmcp import Context as MCPContext
from hanzo_mcp.tools.common.mode import ModeRegistry, register_default_modes
from hanzo_tools.core import BaseTool, auto_timeout
class ModeTool(BaseTool):
"""Tool for managing development modes."""
name = "mode"
def __init__(self):
"""Initialize the mode tool."""
super().__init__()
# Register default modes on initialization
register_default_modes()
@property
@override
def description(self) -> str:
"""Get the tool description."""
return """Manage development modes (programmer personalities). Actions: list (default), activate, switch, show, current, list_presets, select_preset.
Usage:
mode
mode --action list
mode --action activate guido
mode --action switch guido
mode --action show linus
mode --action current
mode --action list_presets
mode --action select_preset --name fullstack"""
@override
async def run(
self,
ctx: MCPContext,
action: str = "list",
name: Optional[str] = None,
) -> str:
"""Manage development modes.
Args:
ctx: MCP context
action: Action to perform (list, activate, show, current)
name: Mode name (for activate/show actions)
Returns:
Action result
"""
if action == "list":
modes = ModeRegistry.list()
if not modes:
return "No modes registered"
output = ["Available development modes (100 programmer personalities):"]
active = ModeRegistry.get_active()
# Group modes by category
categories = {
"Language Creators": [
"guido",
"matz",
"brendan",
"dennis",
"bjarne",
"james",
"anders",
"larry",
"rasmus",
"rich",
],
"Systems & Infrastructure": [
"linus",
"rob",
"ken",
"bill",
"richard",
"brian",
"donald",
"graydon",
"ryan",
"mitchell",
],
"Web & Frontend": [
"tim",
"douglas",
"john",
"evan",
"jordan",
"jeremy",
"david",
"taylor",
"adrian",
"matt",
],
"Database & Data": [
"michael_s",
"michael_w",
"salvatore",
"dwight",
"edgar",
"jim_gray",
"jeff_dean",
"sanjay",
"mike",
"matei",
],
"AI & Machine Learning": [
"yann",
"geoffrey",
"yoshua",
"andrew",
"demis",
"ilya",
"andrej",
"chris",
"francois",
"jeremy_howard",
],
"Security & Cryptography": [
"bruce",
"phil",
"whitfield",
"ralph",
"daniel_b",
"moxie",
"theo",
"dan_kaminsky",
"katie",
"matt_blaze",
],
"Gaming & Graphics": [
"john_carmack",
"sid",
"shigeru",
"gabe",
"markus",
"jonathan",
"casey",
"tim_sweeney",
"hideo",
"will",
],
"Open Source Leaders": [
"miguel",
"nat",
"patrick",
"ian",
"mark_shuttleworth",
"lennart",
"bram",
"daniel_r",
"judd",
"fabrice",
],
"Modern Innovators": [
"vitalik",
"satoshi",
"chris_lattner",
"joe",
"jose",
"sebastian",
"palmer",
"dylan",
"guillermo",
"tom",
],
"Special Configurations": [
"fullstack",
"minimal",
"data_scientist",
"devops",
"security",
"academic",
"startup",
"enterprise",
"creative",
"hanzo",
],
}
for category, mode_names in categories.items():
output.append(f"\n{category}:")
for mode_name in mode_names:
mode = next((m for m in modes if m.name == mode_name), None)
if mode:
marker = (
" (active)" if active and active.name == mode.name else ""
)
output.append(
f" {mode.name}{marker}: {mode.programmer} - {mode.description}"
)
output.append("\nUse 'mode --action activate <name>' to activate a mode")
return "\n".join(output)
elif action in ("activate", "switch"):
if not name:
return "Error: Mode name required for activate action"
try:
ModeRegistry.set_active(name)
mode = ModeRegistry.get(name)
output = [f"Activated mode: {mode.name}"]
output.append(f"Programmer: {mode.programmer}")
output.append(f"Description: {mode.description}")
if mode.philosophy:
output.append(f"Philosophy: {mode.philosophy}")
output.append(f"\nEnabled tools ({len(mode.tools)}):")
# Group tools by category
core_tools = []
package_tools = []
ai_tools = []
search_tools = []
other_tools = []
for tool in sorted(mode.tools):
if tool in [
"read",
"write",
"edit",
"multi_edit",
"bash",
"tree",
"grep",
]:
core_tools.append(tool)
elif tool in ["npx", "uvx", "pip", "cargo", "gem"]:
package_tools.append(tool)
elif tool in ["agent", "consensus", "critic", "think"]:
ai_tools.append(tool)
elif tool in ["search", "symbols", "git_search"]:
search_tools.append(tool)
else:
other_tools.append(tool)
if core_tools:
output.append(f" Core: {', '.join(core_tools)}")
if package_tools:
output.append(f" Package managers: {', '.join(package_tools)}")
if ai_tools:
output.append(f" AI tools: {', '.join(ai_tools)}")
if search_tools:
output.append(f" Search: {', '.join(search_tools)}")
if other_tools:
output.append(f" Specialized: {', '.join(other_tools)}")
if mode.environment:
output.append("\nEnvironment variables:")
for key, value in mode.environment.items():
output.append(f" {key}={value}")
output.append(
"\nNote: Restart MCP session for changes to take full effect"
)
return "\n".join(output)
except ValueError as e:
return str(e)
elif action == "show":
if not name:
return "Error: Mode name required for show action"
mode = ModeRegistry.get(name)
if not mode:
return f"Mode '{name}' not found"
output = [f"Mode: {mode.name}"]
output.append(f"Programmer: {mode.programmer}")
output.append(f"Description: {mode.description}")
if mode.philosophy:
output.append(f"Philosophy: {mode.philosophy}")
output.append(f"\nTools ({len(mode.tools)}):")
for tool in sorted(mode.tools):
output.append(f" - {tool}")
if mode.environment:
output.append("\nEnvironment:")
for key, value in mode.environment.items():
output.append(f" {key}={value}")
return "\n".join(output)
elif action == "current":
active = ModeRegistry.get_active()
if not active:
return "No mode currently active\nUse 'mode --action activate <name>' to activate one"
output = [f"Current mode: {active.name}"]
output.append(f"Programmer: {active.programmer}")
output.append(f"Description: {active.description}")
if active.philosophy:
output.append(f"Philosophy: {active.philosophy}")
output.append(f"Enabled tools: {len(active.tools)}")
return "\n".join(output)
elif action == "list_presets":
modes = ModeRegistry.list()
presets = [m for m in modes if m.name in [
"fullstack", "minimal", "data_scientist", "devops",
"security", "academic", "startup", "enterprise",
"creative", "hanzo",
]]
if not presets:
return "No presets available"
output = ["Available presets:"]
for p in presets:
output.append(f" {p.name}: {p.programmer} - {p.description}")
return "\n".join(output)
elif action == "select_preset":
if not name:
return "Error: Preset name required"
# select_preset is same as activate but only for preset modes
preset_names = [
"fullstack", "minimal", "data_scientist", "devops",
"security", "academic", "startup", "enterprise",
"creative", "hanzo",
]
if name not in preset_names:
return f"Error: '{name}' is not a preset. Use list_presets to see available presets."
try:
ModeRegistry.set_active(name)
mode = ModeRegistry.get(name)
return f"Selected preset: {mode.name} ({mode.programmer})"
except ValueError as e:
return str(e)
else:
return f"Unknown action: {action}. Use 'list', 'activate', 'switch', 'show', 'current', 'list_presets', or 'select_preset'"
def register(self, server: FastMCP) -> None:
"""Register the tool with the MCP server."""
tool_self = self
@server.tool(name=self.name, description=self.description)
async def mode_handler(
ctx: MCPContext, action: str = "list", name: Optional[str] = None
) -> str:
"""Handle mode tool calls."""
return await tool_self.run(ctx, action=action, name=name)
@auto_timeout("mode")
async def call(self, ctx: MCPContext, **params) -> str:
"""Call the tool with arguments."""
return await self.run(
ctx, action=params.get("action", "list"), name=params.get("name")
)
# Create tool instance
mode_tool = ModeTool()