Skip to content

Hotfix (focused scope): patch workflow detection for LLM in VSCode to allow automated execution when exp. setting enabled#6006

Open
James-Cherished wants to merge 6 commits intoKilo-Org:mainfrom
James-Cherished:26-patch-Feb19-add-workflow-detection-to-slash-command-for-AI-execution
Open

Hotfix (focused scope): patch workflow detection for LLM in VSCode to allow automated execution when exp. setting enabled#6006
James-Cherished wants to merge 6 commits intoKilo-Org:mainfrom
James-Cherished:26-patch-Feb19-add-workflow-detection-to-slash-command-for-AI-execution

Conversation

@James-Cherished
Copy link

Context

The experimental setting "Enable model-initiated slash commands" introduced a few months ago never worked.

experimental setting

Comprehensive fixes have been delayed due to LoC, upstream (Roo & Cline) WIP, & kilo rebuild.
This hotfix introduces a minimalist change to immediately make this feature working and available for all users.
Tested locally.

Screenshots

After (intended behavior)

working

Before (current behavior)

not working

Through VSCode extension, the AI only has access to the "init" slash_commands.

How to Test

See scrensoots above.

After

Enable experimental setting
Create a workflow if none: .kilocode/workflows/user-favorite-color-is.md
Start task: "what's user favorite color? only answer using the run_slash_commands tool."
Agent executes the relevant workflow, tells you your favorite color straight away
Workflow provides the agent with new instructions

Before

Start a new chat with the same prompt
Agent won't know, can't know which workflows are available, can only run init, will quit or cheat to answer

References

See
#4760 #5089 #5981

Get in Touch

https://x.com/JamesCherished

Issue: Custom Workflows Not Discovered

Problem Statement

The run_slash_command experimental tool only discovers the built-in "init" command and fails to see custom workflows that appear in the UI autocomplete.

Root Cause: Two Disconnected Systems

The codebase has TWO SEPARATE SYSTEMS for slash commands that are not connected:

System 1: Backend run_slash_command Tool

File: src/core/tools/RunSlashCommandTool.ts

Uses getCommand() from services/command/commands.ts which loads from:

  1. Built-in commands - Only "init" defined in built-in-commands.ts
  2. Global commands - ~/.kilocode/commands/ directory
  3. Project commands - .kilocode/commands/ directory in workspace
// RunSlashCommandTool.ts line 57
const command = await getCommand(task.cwd, commandName)

System 2: Frontend UI Autocomplete

File: webview-ui/src/utils/slash-commands.ts

Uses getSupportedSlashCommands() which includes:

  1. Base commands (hardcoded: newtask, newrule, reportbug, init, smol, etc.)
  2. Mode commands (dynamic mode switching)
  3. Workflow commands from getWorkflowCommands() - reads from .kilocode/workflows/ via ClineRulesToggles
// slash-commands.ts lines 47-49
const workflowCommands = getWorkflowCommands(localWorkflowToggles, globalWorkflowToggles)
return [...baseCommands, ...modeCommands, ...workflowCommands]

The Disconnect

Feature Backend Tool (RunSlashCommandTool) Frontend UI (slash-commands.ts)
Commands dir .kilocode/commands/ Not used
Workflows dir NOT searched .kilocode/workflows/
Workflow toggles NOT used localWorkflowToggles, globalWorkflowToggles
Built-in commands Only "init" Not used for workflows

Key Code Evidence

Backend only searches commands directory:

// services/command/commands.ts lines 136-142
// Scan global commands (override built-in)
const globalDir = path.join(getGlobalRooDirectory(), "commands")
await scanCommandDirectory(globalDir, "global", commands)

// Scan project commands (highest priority)
const projectDir = path.join(getProjectRooDirectoryForCwd(cwd), "commands")
await scanCommandDirectory(projectDir, "project", commands)

Frontend loads workflows from toggles:

// webview-ui/src/utils/slash-commands.ts lines 101-108
function enabledWorkflowToggles(workflowToggles: ClineRulesToggles): SlashCommand[] {
    return Object.entries(workflowToggles)
        .filter(([_, enabled]) => enabled)
        .map(([filePath, _]) => ({
            name: getBasename(filePath),
            section: "custom",
        }))
}

Workflow storage location:

// src/shared/globalFileNames.ts
workflows: ".kilocode/workflows"

Why Only "init" Appears

  1. Built-in commands only define "init" (built-in-commands.ts:10-289)
  2. No custom commands exist in .kilocode/commands/
  3. Workflows in .kilocode/workflows/ are invisible to the backend tool because:
    • getCommand() only searches commands/ directories
    • Workflow toggles (ClineRulesToggles) are never passed to RunSlashCommandTool

Affected Files

File Purpose Issue
src/core/tools/RunSlashCommandTool.ts Tool execution Does not receive workflow toggles
src/services/command/commands.ts Command loading Does not search .kilocode/workflows/
src/services/command/built-in-commands.ts Built-in definitions Only contains "init"
webview-ui/src/utils/slash-commands.ts UI autocomplete Correctly loads workflows
src/core/context/instructions/workflows.ts Workflow management Not connected to command service
src/shared/globalFileNames.ts Path definitions Defines separate commands and workflows paths

Personal thoughts

The legacy from Roo, Cline and Opencode introduced many features and this overlap caused the issue. Slash commands and workflows achieve the same thing, with the only arguable difference to be slash commands to be more user-oriented (type / to instruct the model during the chat), while workflows are more automation-oriented (the AI autonomously loads the necessary workflow). They come from the opposite sides of the user-AI interaction, each with the advantage of being callable from the other side of the equation. (An advantage over Skills, for example)

My current understanding of the code is that the kilo CLI uses built-in slash commands, while the custom slash commands the extension loads (or was supposed to load), as it is implied by the experimental setting, is actually workflows (.kilocode/workflows).

My advice, from what seems the most straightforward and logical:

  • entirely rename slash commands to workflows for the VSCode extension. Avoids confusion, workflows are the only one customizable from the "manage agent behavior" options anyway, typing "/" loads both slash commands and workflows anyway
  • workflows should be model-initiable by default, no need for a toggle since if users create them it's for the AI to use ; if they want something only they trigger with /, it will be slash_commands anyway ; keep the experimental setting to make actual slash_commands model-initiable if you want to push it, there can be a use case for that too.
  • if you go down the extra mile route, add ability to create custom slash commands, usable from the CLI as well

The alternative, if you really want to keep both:

  • in the end I don't even wanna write about the alternative, it would just be a pointless dumb mess

Implementation

This PR implements workflow discoverability and toggle filtering for slash commands, enabling agents to call workflow files in .roo/workflows/ or .kilocode/workflows/ by name while respecting user-defined toggle states.

Changes

1. Workflow Directory Scanning (src/services/command/commands.ts)

The existing scanCommandDirectory() function already handles reading .md files and parsing YAML frontmatter. This change extends getCommands() and getCommand() to also scan workflows/ directories:

// Scan global commands
const globalDir = path.join(getGlobalRooDirectory(), "commands")
await scanCommandDirectory(globalDir, "global", commands)

// Also scan global workflows
const globalWorkflowsDir = path.join(getGlobalRooDirectory(), "workflows")
await scanCommandDirectory(globalWorkflowsDir, "global", commands)

// Scan project commands
const projectDir = path.join(getProjectRooDirectoryForCwd(cwd), "commands")
await scanCommandDirectory(projectDir, "project", commands)

// Also scan project workflows
const projectWorkflowsDir = path.join(getProjectRooDirectoryForCwd(cwd), "workflows")
await scanCommandDirectory(projectWorkflowsDir, "project", commands)

2. Toggle Filtering

Added isEnabled() helper function to filter workflows by toggle state:

function isEnabled(
    filePath: string,
    source: "global" | "project",
    options?: GetCommandOptions,
): boolean {
    if (!options) return true
    const toggles = source === "project" ? options.localToggles : options.globalToggles
    if (!toggles) return true
    return toggles[filePath] !== false // enabled by default if not set
}

3. State Threading (src/core/tools/RunSlashCommandTool.ts)

Retrieve toggles from provider state and pass to getCommand():

const state = await provider?.getState()
const localToggles = state?.workflowToggles?.local ?? {}
const globalToggles = state?.workflowToggles?.global ?? {}

const command = await getCommand(task.cwd, commandName, {
    localToggles,
    globalToggles,
})

Resolution Priority

The command/workflow resolution order is:

  1. Project commands (highest priority)
  2. Global commands
  3. Project workflows (if enabled by toggle)
  4. Global workflows (if enabled by toggle)
  5. Built-in commands

Files Changed

File Change
src/services/command/commands.ts Add workflow dir scanning + toggle filter logic
src/core/tools/RunSlashCommandTool.ts Read toggles from state, pass to getCommand()
src/services/command/__tests__/workflow-toggle.spec.ts New test file for toggle filtering

Testing

  • All 38 existing tests in src/services/command/__tests__/ pass
  • New test file workflow-toggle.spec.ts adds 7 tests covering:
    • Local workflow enabled/disabled/unset toggle states
    • Global workflow enabled/disabled/unset toggle states
    • Commands are not filtered by toggles

Usage

Workflows are enabled by default. A workflow is only excluded when its toggle is explicitly set to false in the state.

Run tests:

cd src && pnpm test services/command/__tests__/

- Updated getCommand() signature in commands.ts to accept toggle options
- Added isEnabled() helper function to filter workflows by toggle state
- Updated RunSlashCommandTool.ts to retrieve toggles from state and pass to getCommand()
- Added tests for workflow toggle filtering
- Created changeset for the feature
- Updated AGENTS.md with new 'Slash Commands & Workflows' section
- Added guidance for using run_slash_command tool
- Explained command naming convention (filename without path/extension)
- Added instruction to explore before declaring impossibility
- Added instruction to trust user direction on tool types
- Updated capabilities.ts system prompt section with same guidance
@changeset-bot
Copy link

changeset-bot bot commented Feb 19, 2026

🦋 Changeset detected

Latest commit: 5961855

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
kilo-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@kiloconnect
Copy link
Contributor

kiloconnect bot commented Feb 19, 2026

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
src/services/command/commands.ts 157 Inconsistent priority: project workflows are scanned after project commands, so a workflow with the same name as a command silently overrides it. In getCommand(), project commands take priority over project workflows (lines 219-227), creating different behavior between listing and lookup.
src/services/command/commands.ts 226 Global commands shadow project workflows: In getCommand(), a disabled project workflow falls through to global commands (line 231), meaning disabling a project workflow could unexpectedly activate a global command with the same name.

SUGGESTION

File Line Issue
src/services/command/commands.ts 147 getCommands() applies toggle filtering to ALL non-built-in sources (including regular commands from commands/ dir), while getCommand() only applies toggle filtering to workflows. Consider making the filtering consistent between the two functions.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/core/tools/RunSlashCommandTool.ts 72 getCommandNames(task.cwd) is called without toggle options when building the error message for unknown commands. This means disabled workflows still appear in the "Available commands" list, which could confuse users who see a command listed but can't execute it. Consider passing toggle options to getCommandNames() (which would require updating its signature to accept GetCommandOptions).
src/services/command/commands.ts 337 getCommandNames() calls getCommands(cwd) without options, so toggle filtering is never applied when listing command names for autocomplete or error messages.
Files Reviewed (5 files)
  • .changeset/workflow-toggle-filtering.md - 0 issues
  • AGENTS.md - 0 issues
  • src/core/prompts/sections/capabilities.ts - 0 issues
  • src/core/tools/RunSlashCommandTool.ts - 0 issues (1 observation in unchanged code)
  • src/services/command/commands.ts - 3 issues
  • src/services/command/__tests__/workflow-toggle.spec.ts - 0 issues

Fix these issues in Kilo Cloud

- Fix inconsistent priority between getCommands() and getCommand()
  - getCommand() now checks project workflows before global commands
  - Ensures project > global priority principle is respected

- Fix global commands shadowing project workflows
  - Reordered lookup: project commands -> project workflows -> global commands -> global workflows

- Add toggle filtering to getCommands()
  - Added optional options parameter for toggle state
  - Built-in commands always pass through
  - Global/project workflows filtered via isEnabled()
@James-Cherished
Copy link
Author

@kilocode-bot can you do a new review? those issues should be fixed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments