Add task sidebar workflow

This commit is contained in:
2026-06-24 12:28:56 +01:00
parent 45263d59f8
commit d133839194
33 changed files with 1890 additions and 134 deletions
+35 -10
View File
@@ -29,7 +29,7 @@ var serverInfo = map[string]any{
"version": "0.1.0",
}
// serverInstructions is returned in the MCP `initialize` response. MCP
// baseServerInstructions is returned in the MCP `initialize` response. MCP
// clients show this to the underlying LLM as context for how to use
// the server. Failure modes we've seen and want to head off:
// - The agent assumes patterm is something it has to launch (running
@@ -45,7 +45,14 @@ var serverInfo = map[string]any{
// up as sub-agents and won't be tied into the patterm lifecycle.
//
// Keep this short — clients vary in how much they surface to the LLM.
const serverInstructions = "You are inside patterm. Use these MCP tools; do not launch patterm or poke its Unix socket yourself. Use spawn_agent for sub-agents, close spawned panes when done, and use timer_fire_when_idle_* instead of wait_for_pattern to wait for send_message replies."
const baseServerInstructions = "You are inside patterm. Use these MCP tools; do not launch patterm or poke its Unix socket yourself. Use spawn_agent for sub-agents, close spawned panes when done, and use timer_fire_when_idle_* instead of wait_for_pattern to wait for send_message replies."
func serverInstructions(taskBound bool) string {
if !taskBound {
return baseServerInstructions
}
return baseServerInstructions + " This MCP connection is task-bound; call whoami for the task, and register git worktrees you create or use with task_register_worktree."
}
// toolDescriptor is the shape returned by `tools/list`. inputSchema is
// a JSON Schema object — we provide a minimal `{type: "object"}` schema
@@ -108,7 +115,7 @@ func arrayOfStringsProp(desc string) map[string]any {
// toolCatalog is the full list advertised via tools/list. Descriptions
// are intentionally short — clients are expected to fetch help() for
// detail. Schemas mirror the param structs in tools.go.
func toolCatalog(role CallerRole) []toolDescriptor {
func toolCatalog(role CallerRole, taskBound bool) []toolDescriptor {
tools := []toolDescriptor{
{
Name: "spawn_agent",
@@ -382,6 +389,14 @@ func toolCatalog(role CallerRole) []toolDescriptor {
"name": stringProp("Scratchpad name."),
}, []string{"name"}),
},
{
Name: "task_register_worktree",
Description: "Register a git worktree path for the current task.",
InputSchema: objectSchema(map[string]any{
"path": stringProp("Absolute path, or path relative to this process working directory."),
"branch": stringProp("Git branch name, if known."),
}, []string{"path"}),
},
{
Name: "whoami",
Description: "Return caller identity, role, parent, and project metadata.",
@@ -397,14 +412,15 @@ func toolCatalog(role CallerRole) []toolDescriptor {
}, nil),
},
}
if role != RoleSubAgent {
return tools
}
filtered := tools[:0]
for _, tool := range tools {
if tool.Name != "spawn_agent" {
filtered = append(filtered, tool)
if role == RoleSubAgent && tool.Name == "spawn_agent" {
continue
}
if !taskBound && tool.Name == "task_register_worktree" {
continue
}
filtered = append(filtered, tool)
}
return filtered
}
@@ -426,13 +442,20 @@ func (s *Server) handleProtocolMethod(callerID, method string, params json.RawMe
if protoVersion == "" {
protoVersion = supportedProtocolVersion
}
taskBound := false
s.mu.Lock()
host := s.host
s.mu.Unlock()
if host != nil {
_, taskBound = host.CallerTask(callerID)
}
result := map[string]any{
"protocolVersion": protoVersion,
"capabilities": map[string]any{
"tools": map[string]any{"listChanged": false},
},
"serverInfo": serverInfo,
"instructions": serverInstructions,
"instructions": serverInstructions(taskBound),
}
return result, true, 0, "", nil
@@ -446,13 +469,15 @@ func (s *Server) handleProtocolMethod(callerID, method string, params json.RawMe
case "tools/list":
role := RoleOrchestrator
taskBound := false
s.mu.Lock()
host := s.host
s.mu.Unlock()
if host != nil {
role = host.CallerRole(callerID)
_, taskBound = host.CallerTask(callerID)
}
return map[string]any{"tools": toolCatalog(role)}, true, 0, "", nil
return map[string]any{"tools": toolCatalog(role, taskBound)}, true, 0, "", nil
case "tools/call":
var p struct {