diff --git a/internal/agent/coordinator.go b/internal/agent/coordinator.go index 562216b5035b03f424782aa6630e545c62899a35..468397c07336fc1b52288462d53a71772ea566d7 100644 --- a/internal/agent/coordinator.go +++ b/internal/agent/coordinator.go @@ -488,7 +488,6 @@ func (c *coordinator) buildTools(ctx context.Context, agent config.Agent, isSubA tools.NewLsTool(c.permissions, c.cfg.WorkingDir(), c.cfg.Config().Tools.Ls), tools.NewSourcegraphTool(nil), tools.NewTodosTool(c.sessions), - tools.NewTouchTool(c.lspManager, c.permissions, c.history, c.filetracker, c.cfg.WorkingDir()), tools.NewViewTool(c.lspManager, c.permissions, c.filetracker, c.skillTracker, c.cfg.WorkingDir(), c.cfg.Config().Options.SkillsPaths...), tools.NewWriteTool(c.lspManager, c.permissions, c.history, c.filetracker, c.cfg.WorkingDir()), ) diff --git a/internal/agent/tools/touch.go b/internal/agent/tools/touch.go deleted file mode 100644 index 3e94e4e381efe097fd31e45a810fadae59b58d2f..0000000000000000000000000000000000000000 --- a/internal/agent/tools/touch.go +++ /dev/null @@ -1,160 +0,0 @@ -package tools - -import ( - "context" - _ "embed" - "fmt" - "os" - "path/filepath" - "strings" - - "charm.land/fantasy" - "github.com/charmbracelet/crush/internal/filepathext" - "github.com/charmbracelet/crush/internal/filetracker" - "github.com/charmbracelet/crush/internal/fsext" - "github.com/charmbracelet/crush/internal/history" - "github.com/charmbracelet/crush/internal/lsp" - "github.com/charmbracelet/crush/internal/permission" -) - -//go:embed touch.md -var touchDescription []byte - -type TouchParams struct { - FilePath string `json:"file_path" description:"The path to the empty file to create"` -} - -type TouchPermissionsParams struct { - FilePath string `json:"file_path"` - OldContent string `json:"old_content,omitempty"` - NewContent string `json:"new_content,omitempty"` -} - -type TouchResponseMetadata struct { - FilePath string `json:"file_path"` -} - -const TouchToolName = "touch" - -func NewTouchTool( - lspManager *lsp.Manager, - permissions permission.Service, - files history.Service, - filetracker filetracker.Service, - workingDir string, -) fantasy.AgentTool { - return fantasy.NewAgentTool( - TouchToolName, - FirstLineDescription(touchDescription), - func(ctx context.Context, params TouchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { - if params.FilePath == "" { - return fantasy.NewTextErrorResponse("file_path is required"), nil - } - - sessionID := GetSessionFromContext(ctx) - if sessionID == "" { - return fantasy.ToolResponse{}, fmt.Errorf("session_id is required") - } - - filePath := filepathext.SmartJoin(workingDir, params.FilePath) - - absWorkingDir, err := filepath.Abs(workingDir) - if err != nil { - return fantasy.ToolResponse{}, fmt.Errorf("error resolving working directory: %w", err) - } - absFilePath, err := filepath.Abs(filePath) - if err != nil { - return fantasy.ToolResponse{}, fmt.Errorf("error resolving file path: %w", err) - } - relPath, relErr := filepath.Rel(absWorkingDir, absFilePath) - isOutsideWorkDir := relErr != nil || strings.HasPrefix(relPath, "..") - - if isOutsideWorkDir { - granted, permReqErr := permissions.Request(ctx, - permission.CreatePermissionRequest{ - SessionID: sessionID, - Path: absFilePath, - ToolCallID: call.ID, - ToolName: TouchToolName, - Action: "write", - Description: fmt.Sprintf("Create empty file outside working directory: %s", absFilePath), - Params: TouchPermissionsParams{ - FilePath: absFilePath, - }, - }, - ) - if permReqErr != nil { - return fantasy.ToolResponse{}, permReqErr - } - if !granted { - return NewPermissionDeniedResponse(), nil - } - } - - fileInfo, err := os.Stat(absFilePath) - if err == nil { - if fileInfo.IsDir() { - return fantasy.NewTextErrorResponse(fmt.Sprintf("Path is a directory, not a file: %s", absFilePath)), nil - } - return fantasy.NewTextErrorResponse(fmt.Sprintf("File already exists: %s", absFilePath)), nil - } else if !os.IsNotExist(err) { - return fantasy.ToolResponse{}, fmt.Errorf("error checking file: %w", err) - } - - dir := filepath.Dir(absFilePath) - if err = os.MkdirAll(dir, 0o755); err != nil { - return fantasy.ToolResponse{}, fmt.Errorf("error creating directory: %w", err) - } - - p, err := permissions.Request(ctx, - permission.CreatePermissionRequest{ - SessionID: sessionID, - Path: fsext.PathOrPrefix(absFilePath, absWorkingDir), - ToolCallID: call.ID, - ToolName: TouchToolName, - Action: "write", - Description: fmt.Sprintf("Create empty file %s", absFilePath), - Params: TouchPermissionsParams{ - FilePath: absFilePath, - OldContent: "", - NewContent: "", - }, - }, - ) - if err != nil { - return fantasy.ToolResponse{}, err - } - if !p { - return NewPermissionDeniedResponse(), nil - } - - file, err := os.OpenFile(absFilePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o644) - if err != nil { - if os.IsExist(err) { - return fantasy.NewTextErrorResponse(fmt.Sprintf("File already exists: %s", absFilePath)), nil - } - return fantasy.ToolResponse{}, fmt.Errorf("error creating file: %w", err) - } - if err = file.Close(); err != nil { - return fantasy.ToolResponse{}, fmt.Errorf("error closing file: %w", err) - } - - _, err = files.Create(ctx, sessionID, absFilePath, "") - if err != nil { - return fantasy.ToolResponse{}, fmt.Errorf("error creating file history: %w", err) - } - - filetracker.RecordRead(ctx, sessionID, absFilePath) - - notifyLSPs(ctx, lspManager, absFilePath) - - result := fmt.Sprintf("Empty file successfully created: %s", absFilePath) - result = fmt.Sprintf("\n%s\n", result) - result += getDiagnostics(absFilePath, lspManager) - return fantasy.WithResponseMetadata(fantasy.NewTextResponse(result), - TouchResponseMetadata{ - FilePath: absFilePath, - }, - ), nil - }) -} diff --git a/internal/agent/tools/touch.md b/internal/agent/tools/touch.md deleted file mode 100644 index 7e0d89ad893c5591b8d911842ae6a6687177ef47..0000000000000000000000000000000000000000 --- a/internal/agent/tools/touch.md +++ /dev/null @@ -1,27 +0,0 @@ -Create an empty file; auto-creates parent dirs. Fails if the file already exists. - - -- Provide file path to create -- Tool creates necessary parent directories automatically - - - -- Creates new empty files -- Auto-creates parent directories if missing -- Refuses to overwrite existing files - - - -- Cannot write content -- Cannot update modification times for existing files -- Cannot create directories - - - -- Use forward slashes (/) for compatibility - - - -- Use Write tool when the file should contain content -- Use LS tool to verify location when creating new files - diff --git a/internal/agent/tools/touch_test.go b/internal/agent/tools/touch_test.go deleted file mode 100644 index feacbeb4616814a942ffb1836e171abb6580cbe5..0000000000000000000000000000000000000000 --- a/internal/agent/tools/touch_test.go +++ /dev/null @@ -1,174 +0,0 @@ -package tools - -import ( - "context" - "encoding/json" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "charm.land/fantasy" - "github.com/charmbracelet/crush/internal/permission" - "github.com/charmbracelet/crush/internal/pubsub" - "github.com/stretchr/testify/require" -) - -// recordingPermissionService captures permission requests and answers them -// according to a configurable response. -type recordingPermissionService struct { - *pubsub.Broker[permission.PermissionRequest] - requests []permission.CreatePermissionRequest - grant bool -} - -func (m *recordingPermissionService) Request(ctx context.Context, req permission.CreatePermissionRequest) (bool, error) { - m.requests = append(m.requests, req) - return m.grant, nil -} - -func (m *recordingPermissionService) Grant(req permission.PermissionRequest) {} -func (m *recordingPermissionService) Deny(req permission.PermissionRequest) {} -func (m *recordingPermissionService) GrantPersistent(req permission.PermissionRequest) {} -func (m *recordingPermissionService) AutoApproveSession(sessionID string) {} -func (m *recordingPermissionService) SetSkipRequests(skip bool) {} -func (m *recordingPermissionService) SkipRequests() bool { return false } -func (m *recordingPermissionService) SubscribeNotifications(ctx context.Context) <-chan pubsub.Event[permission.PermissionNotification] { - return make(<-chan pubsub.Event[permission.PermissionNotification]) -} - -type mockFileTrackerService struct{} - -func (m mockFileTrackerService) RecordRead(ctx context.Context, sessionID, path string) {} - -func (m mockFileTrackerService) LastReadTime(ctx context.Context, sessionID, path string) time.Time { - return time.Now() -} - -func (m mockFileTrackerService) ListReadFiles(ctx context.Context, sessionID string) ([]string, error) { - return nil, nil -} - -func TestTouchToolCreatesEmptyFile(t *testing.T) { - t.Parallel() - - workingDir := t.TempDir() - tool := NewTouchTool(nil, &mockPermissionService{}, &mockHistoryService{}, mockFileTrackerService{}, workingDir) - ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session") - - resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "nested/empty.txt"}) - require.False(t, resp.IsError) - - filePath := filepath.Join(workingDir, "nested", "empty.txt") - info, err := os.Stat(filePath) - require.NoError(t, err) - require.False(t, info.IsDir()) - require.Zero(t, info.Size()) -} - -func TestTouchToolRefusesExistingFile(t *testing.T) { - t.Parallel() - - workingDir := t.TempDir() - filePath := filepath.Join(workingDir, "existing.txt") - require.NoError(t, os.WriteFile(filePath, []byte("content"), 0o644)) - - tool := NewTouchTool(nil, &mockPermissionService{}, &mockHistoryService{}, mockFileTrackerService{}, workingDir) - ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session") - - resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "existing.txt"}) - require.True(t, resp.IsError) - require.Contains(t, resp.Content, "File already exists") - - content, err := os.ReadFile(filePath) - require.NoError(t, err) - require.Equal(t, "content", string(content)) -} - -func TestTouchToolStaysInsideWorkingDir(t *testing.T) { - t.Parallel() - - workingDir := t.TempDir() - perms := &recordingPermissionService{grant: true} - tool := NewTouchTool(nil, perms, &mockHistoryService{}, mockFileTrackerService{}, workingDir) - ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session") - - resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "inside.txt"}) - require.False(t, resp.IsError) - - for _, req := range perms.requests { - require.NotContains(t, req.Description, "outside working directory", - "inside-workingDir touch should not trigger an outside-workingDir permission prompt") - } - - _, err := os.Stat(filepath.Join(workingDir, "inside.txt")) - require.NoError(t, err) -} - -func TestTouchToolOutsideWorkingDirRequiresPermission(t *testing.T) { - t.Parallel() - - parent := t.TempDir() - workingDir := filepath.Join(parent, "wd") - require.NoError(t, os.MkdirAll(workingDir, 0o755)) - - // Denied: file outside workingDir must not be created. - deny := &recordingPermissionService{grant: false} - tool := NewTouchTool(nil, deny, &mockHistoryService{}, mockFileTrackerService{}, workingDir) - ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session") - - resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "../escape.txt"}) - require.True(t, resp.IsError) - - require.Len(t, deny.requests, 1) - require.True(t, strings.Contains(deny.requests[0].Description, "outside working directory"), - "expected outside-working-directory permission prompt, got %q", deny.requests[0].Description) - - _, err := os.Stat(filepath.Join(parent, "escape.txt")) - require.True(t, os.IsNotExist(err), "denied permission should not create the file") - - // Granted: same path now succeeds. - grant := &recordingPermissionService{grant: true} - tool = NewTouchTool(nil, grant, &mockHistoryService{}, mockFileTrackerService{}, workingDir) - resp = runTouchTool(t, tool, ctx, TouchParams{FilePath: "../escape.txt"}) - require.False(t, resp.IsError) - require.GreaterOrEqual(t, len(grant.requests), 1) - require.Contains(t, grant.requests[0].Description, "outside working directory") - - _, err = os.Stat(filepath.Join(parent, "escape.txt")) - require.NoError(t, err) -} - -func TestWriteToolEmptyContentPointsToTouch(t *testing.T) { - t.Parallel() - - tool := NewWriteTool(nil, nil, nil, nil, t.TempDir()) - - input, err := json.Marshal(WriteParams{FilePath: "empty.txt"}) - require.NoError(t, err) - - resp, err := tool.Run(context.Background(), fantasy.ToolCall{ - ID: "test-call", - Name: WriteToolName, - Input: string(input), - }) - require.NoError(t, err) - require.True(t, resp.IsError) - require.Equal(t, `content is required. use the "touch" tool to create an empty file`, resp.Content) -} - -func runTouchTool(t *testing.T, tool fantasy.AgentTool, ctx context.Context, params TouchParams) fantasy.ToolResponse { - t.Helper() - - input, err := json.Marshal(params) - require.NoError(t, err) - - resp, err := tool.Run(ctx, fantasy.ToolCall{ - ID: "test-call", - Name: TouchToolName, - Input: string(input), - }) - require.NoError(t, err) - return resp -} diff --git a/internal/agent/tools/write.go b/internal/agent/tools/write.go index 2d936d75bc12c643fd721e74bb4008d445c6aa13..ce243b452059b396682d9f3c2773c69199b9f2a5 100644 --- a/internal/agent/tools/write.go +++ b/internal/agent/tools/write.go @@ -58,10 +58,6 @@ func NewWriteTool( return fantasy.NewTextErrorResponse("file_path is required"), nil } - if params.Content == "" { - return fantasy.NewTextErrorResponse(`content is required. use the "touch" tool to create an empty file`), nil - } - sessionID := GetSessionFromContext(ctx) if sessionID == "" { return fantasy.ToolResponse{}, fmt.Errorf("session_id is required") diff --git a/internal/agent/tools/write.md b/internal/agent/tools/write.md index fbe58a5648911375d8ae002fa9a1a83c7d81a305..922f3796172d104cad716bdd8b6db3228144e281 100644 --- a/internal/agent/tools/write.md +++ b/internal/agent/tools/write.md @@ -2,7 +2,7 @@ Create or overwrite a file with given content; auto-creates parent dirs. Cannot - Provide file path to write -- Include content to write to file +- Include content to write to file (may be empty for a new empty file) - Tool creates necessary parent directories automatically diff --git a/internal/agent/tools/write_test.go b/internal/agent/tools/write_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7b63bfc57c4de4ce66a0a00b7611041ee7238436 --- /dev/null +++ b/internal/agent/tools/write_test.go @@ -0,0 +1,49 @@ +package tools + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "testing" + "time" + + "charm.land/fantasy" + "github.com/stretchr/testify/require" +) + +type mockFileTrackerService struct{} + +func (m mockFileTrackerService) RecordRead(ctx context.Context, sessionID, path string) {} + +func (m mockFileTrackerService) LastReadTime(ctx context.Context, sessionID, path string) time.Time { + return time.Now() +} + +func (m mockFileTrackerService) ListReadFiles(ctx context.Context, sessionID string) ([]string, error) { + return nil, nil +} + +func TestWriteToolWritesEmptyNewFile(t *testing.T) { + t.Parallel() + + workingDir := t.TempDir() + ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session") + + tool := NewWriteTool(nil, &mockPermissionService{}, &mockHistoryService{}, mockFileTrackerService{}, workingDir) + + input, err := json.Marshal(WriteParams{FilePath: "empty.txt", Content: ""}) + require.NoError(t, err) + + resp, err := tool.Run(ctx, fantasy.ToolCall{ + ID: "test-call", + Name: WriteToolName, + Input: string(input), + }) + require.NoError(t, err) + require.False(t, resp.IsError) + + b, err := os.ReadFile(filepath.Join(workingDir, "empty.txt")) + require.NoError(t, err) + require.Equal(t, "", string(b)) +} diff --git a/internal/config/config.go b/internal/config/config.go index d93b9c95ebd399fedddcc9158db77427fcf28da2..b7f612a9f28e88e8ff9c6e430c3f471b401ebe57 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -674,7 +674,6 @@ func allToolNames() []string { "ls", "sourcegraph", "todos", - "touch", "view", "write", "list_mcp_resources", diff --git a/internal/config/load_test.go b/internal/config/load_test.go index 5ade7d8e4cd244263bcf64f9018a619e755dedd7..61054e5ccbc9031702f3d1d778634bf53b625bcb 100644 --- a/internal/config/load_test.go +++ b/internal/config/load_test.go @@ -490,7 +490,7 @@ func TestConfig_setupAgentsWithDisabledTools(t *testing.T) { coderAgent, ok := cfg.Agents[AgentCoder] require.True(t, ok) - assert.Equal(t, []string{"agent", "bash", "crush_info", "crush_logs", "job_output", "job_kill", "multiedit", "lsp_diagnostics", "lsp_references", "lsp_restart", "fetch", "agentic_fetch", "glob", "ls", "sourcegraph", "todos", "touch", "view", "write", "list_mcp_resources", "read_mcp_resource"}, coderAgent.AllowedTools) + assert.Equal(t, []string{"agent", "bash", "crush_info", "crush_logs", "job_output", "job_kill", "multiedit", "lsp_diagnostics", "lsp_references", "lsp_restart", "fetch", "agentic_fetch", "glob", "ls", "sourcegraph", "todos", "view", "write", "list_mcp_resources", "read_mcp_resource"}, coderAgent.AllowedTools) taskAgent, ok := cfg.Agents[AgentTask] require.True(t, ok) @@ -513,7 +513,7 @@ func TestConfig_setupAgentsWithEveryReadOnlyToolDisabled(t *testing.T) { cfg.SetupAgents() coderAgent, ok := cfg.Agents[AgentCoder] require.True(t, ok) - assert.Equal(t, []string{"agent", "bash", "crush_info", "crush_logs", "job_output", "job_kill", "download", "edit", "multiedit", "lsp_diagnostics", "lsp_references", "lsp_restart", "fetch", "agentic_fetch", "todos", "touch", "write", "list_mcp_resources", "read_mcp_resource"}, coderAgent.AllowedTools) + assert.Equal(t, []string{"agent", "bash", "crush_info", "crush_logs", "job_output", "job_kill", "download", "edit", "multiedit", "lsp_diagnostics", "lsp_references", "lsp_restart", "fetch", "agentic_fetch", "todos", "write", "list_mcp_resources", "read_mcp_resource"}, coderAgent.AllowedTools) taskAgent, ok := cfg.Agents[AgentTask] require.True(t, ok) diff --git a/internal/proto/permission.go b/internal/proto/permission.go index 981a2c22c812c322adb44ad5a11e8392af69994b..5834de628e41a290d0bc391fbe3ead2505eb742a 100644 --- a/internal/proto/permission.go +++ b/internal/proto/permission.go @@ -100,12 +100,6 @@ func unmarshalToolParams(toolName string, raw json.RawMessage) (any, error) { return nil, err } return params, nil - case TouchToolName: - var params TouchPermissionsParams - if err := json.Unmarshal(raw, ¶ms); err != nil { - return nil, err - } - return params, nil case WriteToolName: var params WritePermissionsParams if err := json.Unmarshal(raw, ¶ms); err != nil { diff --git a/internal/proto/tools.go b/internal/proto/tools.go index cee9e70f11032d5b9d620c43214adcd31125480d..09774ac0a22b672ff7df81d968db21ef35517c02 100644 --- a/internal/proto/tools.go +++ b/internal/proto/tools.go @@ -227,25 +227,6 @@ type ViewResponseMetadata struct { Content string `json:"content"` } -const TouchToolName = "touch" - -// TouchParams represents the parameters for the touch tool. -type TouchParams struct { - FilePath string `json:"file_path"` -} - -// TouchPermissionsParams represents the permission parameters for the touch tool. -type TouchPermissionsParams struct { - FilePath string `json:"file_path"` - OldContent string `json:"old_content,omitempty"` - NewContent string `json:"new_content,omitempty"` -} - -// TouchResponseMetadata represents the metadata for the touch tool response. -type TouchResponseMetadata struct { - FilePath string `json:"file_path"` -} - const WriteToolName = "write" // WriteParams represents the parameters for the write tool. diff --git a/internal/ui/chat/file.go b/internal/ui/chat/file.go index c47c7c2fc955122299e82f6adc2167996d11d27f..14fc5169aec1b0a238cad177a0be5bf4d6db27b0 100644 --- a/internal/ui/chat/file.go +++ b/internal/ui/chat/file.go @@ -97,55 +97,6 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * return joinToolParts(header, body) } -// ----------------------------------------------------------------------------- -// Touch Tool -// ----------------------------------------------------------------------------- - -// TouchToolMessageItem is a message item that represents a touch tool call. -type TouchToolMessageItem struct { - *baseToolMessageItem -} - -var _ ToolMessageItem = (*TouchToolMessageItem)(nil) - -// NewTouchToolMessageItem creates a new [TouchToolMessageItem]. -func NewTouchToolMessageItem( - sty *styles.Styles, - toolCall message.ToolCall, - result *message.ToolResult, - canceled bool, -) ToolMessageItem { - return newBaseToolMessageItem(sty, toolCall, result, &TouchToolRenderContext{}, canceled) -} - -// TouchToolRenderContext renders touch tool messages. -type TouchToolRenderContext struct{} - -// RenderTool implements the [ToolRenderer] interface. -func (t *TouchToolRenderContext) RenderTool(sty *styles.Styles, width int, opts *ToolRenderOpts) string { - cappedWidth := cappedMessageWidth(width) - if opts.IsPending() { - return pendingTool(sty, "Touch", opts.Anim, opts.Compact) - } - - var params tools.TouchParams - if err := json.Unmarshal([]byte(opts.ToolCall.Input), ¶ms); err != nil { - return toolErrorContent(sty, &message.ToolResult{Content: "Invalid parameters"}, cappedWidth) - } - - file := fsext.PrettyPath(params.FilePath) - header := toolHeader(sty, opts.Status, "Touch", cappedWidth, opts.Compact, file) - if opts.Compact { - return header - } - - if earlyState, ok := toolEarlyStateContent(sty, opts, cappedWidth); ok { - return joinToolParts(header, earlyState) - } - - return header -} - // ----------------------------------------------------------------------------- // Write Tool // ----------------------------------------------------------------------------- diff --git a/internal/ui/chat/tools.go b/internal/ui/chat/tools.go index 80e4e16954f4d83c22bbcc60583ebdb3eb02cee2..4715cfafa6a25f01b93ee56caf41aa55aee3196e 100644 --- a/internal/ui/chat/tools.go +++ b/internal/ui/chat/tools.go @@ -221,8 +221,6 @@ func NewToolMessageItem( item = NewJobKillToolMessageItem(sty, toolCall, result, canceled) case tools.ViewToolName: item = NewViewToolMessageItem(sty, toolCall, result, canceled) - case tools.TouchToolName: - item = NewTouchToolMessageItem(sty, toolCall, result, canceled) case tools.WriteToolName: item = NewWriteToolMessageItem(sty, toolCall, result, canceled) case tools.EditToolName: @@ -1078,11 +1076,6 @@ func (t *baseToolMessageItem) formatParametersForCopy() string { parts = append(parts, fmt.Sprintf("**Edits:** %d", len(params.Edits))) return strings.Join(parts, "\n") } - case tools.TouchToolName: - var params tools.TouchParams - if json.Unmarshal([]byte(t.toolCall.Input), ¶ms) == nil { - return fmt.Sprintf("**File:** %s", fsext.PrettyPath(params.FilePath)) - } case tools.WriteToolName: var params tools.WriteParams if json.Unmarshal([]byte(t.toolCall.Input), ¶ms) == nil { @@ -1224,8 +1217,6 @@ func (t *baseToolMessageItem) formatResultForCopy() string { return t.formatEditResultForCopy() case tools.MultiEditToolName: return t.formatMultiEditResultForCopy() - case tools.TouchToolName: - return t.formatTouchResultForCopy() case tools.WriteToolName: return t.formatWriteResultForCopy() case tools.FetchToolName: @@ -1399,20 +1390,6 @@ func (t *baseToolMessageItem) formatMultiEditResultForCopy() string { return result.String() } -// formatTouchResultForCopy formats touch tool results for clipboard. -func (t *baseToolMessageItem) formatTouchResultForCopy() string { - if t.result == nil { - return "" - } - - var params tools.TouchParams - if json.Unmarshal([]byte(t.toolCall.Input), ¶ms) != nil { - return t.result.Content - } - - return fmt.Sprintf("File: %s\n```\n```", fsext.PrettyPath(params.FilePath)) -} - // formatWriteResultForCopy formats write tool results for clipboard. func (t *baseToolMessageItem) formatWriteResultForCopy() string { if t.result == nil { @@ -1598,8 +1575,6 @@ func prettifyToolName(name string) string { return "Sourcegraph" case tools.TodosToolName: return "To-Do" - case tools.TouchToolName: - return "Touch" case tools.ViewToolName: return "View" case tools.WriteToolName: diff --git a/internal/ui/dialog/permissions.go b/internal/ui/dialog/permissions.go index 01097fcc6e6145b26ffa711a91e617aef39f3816..c130f14b4b3c0a42929c1bbbf5447dc8e14aa303 100644 --- a/internal/ui/dialog/permissions.go +++ b/internal/ui/dialog/permissions.go @@ -316,7 +316,7 @@ func (p *Permissions) respond(action PermissionAction) tea.Msg { func (p *Permissions) hasDiffView() bool { switch p.permission.ToolName { - case tools.EditToolName, tools.TouchToolName, tools.WriteToolName, tools.MultiEditToolName: + case tools.EditToolName, tools.WriteToolName, tools.MultiEditToolName: return true } return false @@ -463,13 +463,11 @@ func (p *Permissions) renderHeader(contentWidth int) string { lines = append(lines, p.renderKeyValue("URL", params.URL, contentWidth)) lines = append(lines, p.renderKeyValue("File", fsext.PrettyPath(params.FilePath), contentWidth)) } - case tools.EditToolName, tools.TouchToolName, tools.WriteToolName, tools.MultiEditToolName, tools.ViewToolName: + case tools.EditToolName, tools.WriteToolName, tools.MultiEditToolName, tools.ViewToolName: var filePath string switch params := p.permission.Params.(type) { case tools.EditPermissionsParams: filePath = params.FilePath - case tools.TouchPermissionsParams: - filePath = params.FilePath case tools.WritePermissionsParams: filePath = params.FilePath case tools.MultiEditPermissionsParams: @@ -529,8 +527,6 @@ func (p *Permissions) renderContent(width int) string { return p.renderBashContent(width) case tools.EditToolName: return p.renderEditContent(width) - case tools.TouchToolName: - return p.renderTouchContent(width) case tools.WriteToolName: return p.renderWriteContent(width) case tools.MultiEditToolName: @@ -567,14 +563,6 @@ func (p *Permissions) renderEditContent(contentWidth int) string { return p.renderDiff(params.FilePath, params.OldContent, params.NewContent, contentWidth) } -func (p *Permissions) renderTouchContent(contentWidth int) string { - params, ok := p.permission.Params.(tools.TouchPermissionsParams) - if !ok { - return "" - } - return p.renderDiff(params.FilePath, params.OldContent, params.NewContent, contentWidth) -} - func (p *Permissions) renderWriteContent(contentWidth int) string { params, ok := p.permission.Params.(tools.WritePermissionsParams) if !ok {