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 {