The Problem With Command Palettes
You press Ctrl+Shift+A and expect magic. Most command palettes deliver a flat list of 20-30 actions, alphabetized, searchable only by exact name. Want to "move a document"? You need to know the command is called "Move Document to Workspace" not "Send to Folder" or "Relocate File." You're fighting the tool's vocabulary instead of just saying what you want.
AiFiler's Universal Command is different. It doesn't force you into a predefined command vocabulary. Instead, it understands intent—what you're trying to accomplish—and routes you to the right action, even if you phrase it differently.
The challenge: how do you build a system that understands 50+ different intents expressed in infinite ways?
Architecture: Intent → Heuristics → Handler → Execution
The Universal Command system sits at lib/intelligence/universalRouter.ts and orchestrates a three-stage pipeline:
Stage 1: Intent Detection → Your input is analyzed for semantic meaning Stage 2: Heuristic Matching → The detected intent is matched against 87 registered intent handlers Stage 3: Action Execution → The appropriate handler executes the action
Here's the data flow:
User Input (natural language)
↓
Intent Heuristics Parser (lib/intentHeuristics.ts)
↓
Intent Classification (e.g., "move_document", "create_workspace")
↓
Universal Router (lib/intelligence/universalRouter.ts)
↓
Intent Handler Lookup (lib/intelligence/intentHandlers.ts — 87 handlers)
↓
Action Executor (lib/intelligence/actionExecutor.ts)
↓
Supabase API / AI Client / Local State Update
↓
UI Feedback (toast, modal, redirect)
This separation of concerns means each layer can fail gracefully. If intent detection is uncertain, the system can ask for clarification rather than executing the wrong action.
Intent Heuristics: The Pattern Matching Engine
The real work happens in lib/intentHeuristics.ts. This file contains pattern matchers that extract intent from free-form text. Instead of regex soup, AiFiler uses a heuristic scoring system.
For example, if you type "move this doc to client files," the heuristic engine:
- Tokenizes the input:
["move", "this", "doc", "to", "client", "files"] - Scores against known patterns:
- "move" + object + "to" + destination = high confidence for
move_documentintent - "doc" is recognized as a synonym for "document"
- "client files" is parsed as a potential workspace/folder reference
- "move" + object + "to" + destination = high confidence for
- Extracts parameters: The system identifies what's being moved (current document context) and where it's going ("client files")
- Returns structured intent:
{ "intent": "move_document", "confidence": 0.92, "parameters": { "targetWorkspace": "client files", "sourceId": "doc_xyz" } }
The beauty here is confidence scoring. If confidence is below a threshold (say, 0.7), the system doesn't guess—it asks you to clarify. "Did you mean move to 'Client Files' workspace?" This prevents silent failures where the wrong action executes.
The Intent Handler Registry: 87 Handlers, One Router
lib/intelligence/intentHandlers.ts is the dispatch table. It's not a giant switch statement—it's a registry pattern where each intent has a corresponding handler object:
const intentHandlers = {
move_document: {
name: "Move Document",
description: "Move a document to a different workspace",
handler: async (params, context) => {
// Validate params
// Call Supabase to update document.workspace_id
// Return success/error
}
},
create_workspace: {
name: "Create Workspace",
description: "Create a new workspace",
handler: async (params, context) => {
// Create workspace logic
}
},
// ... 85 more handlers
}
Each handler receives:
- params: Extracted from the user input (e.g.,
{ targetWorkspace: "..." }) - context: Current user, active document, workspace state (from
lib/workspace/)
This design scales. Adding a new intent is as simple as:
- Add a heuristic pattern to detect it
- Register a handler in the intent handlers file
- Done
The router doesn't care how many handlers exist. It's just a lookup and execute.
Real-World Example: "Archive these old contracts"
Let's trace a real command through the system:
You type: archive these old contracts
Intent Detection:
- Heuristics see "archive" (action) + "these" (current context) + "old contracts" (filter)
- Confidence: 0.85 for
batch_archiveintent - Parameters extracted:
{ filter: "old contracts", scope: "current_selection" }
Handler Lookup:
- Router finds
intentHandlers.batch_archive
Execution:
- Handler queries Supabase: "Find documents matching 'old contracts' in current workspace"
- Returns 23 matching documents
- Calls
UPDATE documents SET archived = true WHERE id IN (...) - Returns success: "Archived 23 documents"
UI Feedback:
- Toast notification: "23 documents archived"
- Matrix (formerly Tables) view updates to hide archived rows
- User can undo via the action history
The entire flow takes ~200ms. No modal, no confirmation dialog (unless archiving is destructive—then it asks). The system trusts the intent detection but provides undo.
Why Confidence Matters More Than Certainty
Most command systems are binary: either they understand or they don't. AiFiler's confidence scoring allows for graceful degradation.
Low confidence (0.5–0.7)? Ask for clarification:
"Did you mean 'Move to Client Files' workspace? (Yes / No / Cancel)"
Medium confidence (0.7–0.85)? Execute but show what you're doing:
"Moving 5 documents to 'Client Files'..." [Undo]
High confidence (0.85+)? Execute silently and provide undo:
[Toast] "5 documents moved"
This prevents the two failure modes:
- False positives: Executing the wrong action silently
- False negatives: Requiring confirmation for obvious intents
The Context Layer: Why "This" Matters
The context parameter passed to handlers is crucial. When you say "archive this," the system needs to know what "this" is.
Context includes:
- activeMatrix: The currently visible document list (from
lib/workspace/) - selectedRows: Which documents are selected
- currentWorkspace: The active workspace
- user: Authentication state
- recentDocuments: For "open recent" style intents
This is why AiFiler's Universal Command feels intelligent. It doesn't just parse your words—it understands your position in the app.
Lessons Learned: What Doesn't Scale
Early versions tried to handle everything with regex patterns. That broke fast:
- "Move doc to folder" vs. "Move document to workspace" vs. "Send to folder"
- Each variation needed a separate pattern
- Patterns interfered with each other
The solution: semantic grouping. Instead of matching exact phrases, group similar intents and let the handler extract parameters. "Move X to Y" is one intent, regardless of whether X is "doc" or "document" or "file."
We also learned that undo is non-negotiable. Users will test the system's boundaries. If they say "delete all" as a joke and it actually deletes, trust is gone. Every handler that modifies data supports undo via Supabase's audit log.
What This Means For You
The Universal Command (Ctrl+Shift+A) is designed to feel like a conversation, not a menu. You don't memorize commands—you just say what you want. The system understands context, handles ambiguity gracefully, and gives you undo.
For power users, this means:
- Type naturally. "Move these to client work" works as well as "Move Document to Workspace"
- Chain intents. "Archive old contracts, then create a summary" can be a single command
- Rely on undo. The system assumes you might change your mind
For teams building on AiFiler's API, the intent handler pattern is a template. You can register custom intents for your workflows. The router doesn't care—it's just a dispatcher.
The real insight: intent detection beats command memorization. Every minute you don't spend looking for the right command name is a minute you spend on actual work.
Enjoyed this article?
Get more articles like this delivered to your inbox. No spam, unsubscribe anytime.