Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2006,3 +2006,26 @@ task_name: whitespace-task
})
}
}

// TestNonExistentSlashCommandPassesThrough verifies that a task referencing a
// slash command that does not exist passes through the text as-is instead of erroring.
func TestNonExistentSlashCommandPassesThrough(t *testing.T) {
t.Parallel()
dirs := setupTestDirs(t)

// Create a task that references a non-existent slash command
taskContent := `# My Task with a File Path
/home/coder/.config should be ignored
`
taskFile := filepath.Join(dirs.tasksDir, "bad-command-task.md")
if err := os.WriteFile(taskFile, []byte(taskContent), 0o600); err != nil {
t.Fatalf("failed to write task file: %v", err)
}

output := runTool(t, "-C", dirs.tmpDir, "bad-command-task")

if !strings.Contains(output, "/home/coder/.config should be ignored") {
t.Errorf("expected non-existent slash command to pass through as-is, got: %s", output)
}
}
13 changes: 9 additions & 4 deletions pkg/codingcontext/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,16 @@ func (cc *Context) processTaskBlock(block taskparser.Block, path string, expandP
if block.SlashCommand != nil {
commandContent, err := cc.findCommand(block.SlashCommand.Name, block.SlashCommand.Params())
if err != nil {
if cc.lintMode && errors.Is(err, ErrCommandNotFound) {
cc.lintCollector.recordError(path, LintErrorKindMissingCommand,
"command not found: "+block.SlashCommand.Name)
if errors.Is(err, ErrCommandNotFound) {
if cc.lintMode {
cc.lintCollector.recordError(path, LintErrorKindMissingCommand,
"command not found: "+block.SlashCommand.Name)
} else {
cc.logger.Warn("Command not found, passing through as-is",
"command", block.SlashCommand.Name)
}

return "/" + block.SlashCommand.Name, nil
return block.SlashCommand.String(), nil
}

return "", fmt.Errorf("failed to find command %s: %w", block.SlashCommand.Name, err)
Expand Down
44 changes: 32 additions & 12 deletions pkg/codingcontext/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -957,14 +957,19 @@ func TestContext_Run_Commands(t *testing.T) {
check: checkTaskNotEmpty,
},
{
name: "command not found returns error",
name: "command not found passes through as-is",
setup: func(t *testing.T, dir string) {
t.Helper()
createTask(t, dir, "missing-cmd", "", "/nonexistent")
},
taskName: "missing-cmd",
wantErr: true,
errContains: "command not found",
taskName: "missing-cmd",
wantErr: false,
check: func(t *testing.T, result *Result) {
t.Helper()
if !strings.Contains(result.Prompt, "/nonexistent") {
t.Errorf("expected pass-through of /nonexistent, got %q", result.Prompt)
}
},
},
{
name: "command parameter overrides context parameter",
Expand Down Expand Up @@ -1249,16 +1254,22 @@ func TestContext_Run_Errors(t *testing.T) {
taskName string
wantErr bool
errContains string
check func(t *testing.T, result *Result)
}{
{
name: "command not found in task",
name: "command not found in task passes through as-is",
setup: func(t *testing.T, dir string) {
t.Helper()
createTask(t, dir, "bad-cmd", "", "/missing-command\n")
},
taskName: "bad-cmd",
wantErr: true,
errContains: "command not found",
taskName: "bad-cmd",
wantErr: false,
check: func(t *testing.T, result *Result) {
t.Helper()
if !strings.Contains(result.Prompt, "/missing-command") {
t.Errorf("expected pass-through of /missing-command, got %q", result.Prompt)
}
},
},
{
name: "invalid agent in task frontmatter",
Expand Down Expand Up @@ -1298,6 +1309,10 @@ func TestContext_Run_Errors(t *testing.T) {
t.Errorf("expected error to contain %q, got %v", tt.errContains, err)
}
}

if !tt.wantErr && tt.check != nil {
tt.check(t, result)
}
})
}
}
Expand Down Expand Up @@ -1839,17 +1854,22 @@ func TestUserPrompt(t *testing.T) {
check: checkTaskContains("${issue_number}"), // expand:false applies to user_prompt too
},
{
name: "user_prompt with invalid slash command",
name: "user_prompt with invalid slash command passes through as-is",
setup: func(t *testing.T, dir string) {
t.Helper()
createTask(t, dir, "invalid", "", "Task content\n")
},
opts: []Option{
WithUserPrompt("/nonexistent-command\n"),
},
taskName: "invalid",
wantErr: true,
errContains: "command not found",
taskName: "invalid",
wantErr: false,
check: func(t *testing.T, result *Result) {
t.Helper()
if !strings.Contains(result.Prompt, "/nonexistent-command") {
t.Errorf("expected pass-through of /nonexistent-command, got %q", result.Prompt)
}
},
},
{
name: "both task prompt and user prompt parse correctly",
Expand Down