The Problem With Every Other Command Palette
You open the command palette. You type three letters. The system narrows down to 47 possible actions. Now what? Does it guess? Does it wait for you to click? Does it execute something you didn't ask for?
Most command systems are built backwards. They start with UI buttons, then wrap them in a search interface. The result: a feature that feels bolted on, not native. Users learn a few keyboard shortcuts and abandon the command palette entirely.
AiFiler's Universal Command (Ctrl+Shift+A) works differently. It doesn't search for actions—it understands intent. When you type "summarize this," it doesn't hunt through a list of 50 button labels. It parses what you actually want to do, figures out what context you're in, and executes the right handler. No guessing. No extra clicks.
Here's how we built that system, and why the architecture matters.
Intent Detection: From Text to Meaning
The first challenge: how do you convert free-form text into structured intent?
Most command systems use fuzzy string matching. You type "cre" and it shows you "Create," "Credentials," "Create Template." Fast, but fragile. It breaks the moment users phrase things differently.
We use a two-stage detection pipeline:
Stage 1: Pattern Matching (Fast Path)
For common, predictable intents, we use regex patterns. "Create a template" → CREATE_TEMPLATE. "Summarize this" → SUMMARIZE. "Share with" → SHARE. These patterns live in lib/intelligence/intentHandlers.ts and execute in microseconds.
// Simplified example of pattern matching
const patterns = {
CREATE_TEMPLATE: /^create\s+(?:a\s+)?template/i,
SUMMARIZE: /^summarize\s+(?:this|document)?/i,
SHARE: /^share\s+(?:with|to)/i,
EXPORT: /^export\s+(?:as|to)\s+(\w+)/i,
};
Stage 2: Semantic Understanding (Fallback) For ambiguous or complex queries, we send the input to Claude with a structured prompt. Claude sees the list of available intents and the current context (what document is open, what workspace you're in, etc.) and returns the most likely intent.
This two-stage approach is crucial: 95% of queries hit the fast path and return in <50ms. The remaining 5% that need semantic reasoning still complete in <500ms because we've already narrowed the scope.
The Universal Router: Intent → Handler
Once we know the intent, we need to execute it. This is where most systems fail—they build a massive switch statement or a chain of if-else conditions. Maintainable for 10 intents. Unmaintainable for 50+.
We built lib/intelligence/universalRouter.ts as a registry pattern. Here's the concept:
// Simplified structure
const intentHandlers = {
CREATE_TEMPLATE: handleCreateTemplate,
SUMMARIZE: handleSummarize,
SHARE: handleShare,
EXPORT: handleExport,
// ... 46 more handlers
};
export async function executeIntent(intent, context, params) {
const handler = intentHandlers[intent];
if (!handler) throw new Error(`Unknown intent: ${intent}`);
return handler(context, params);
}
This is standard registry pattern stuff, but the real complexity is in what each handler receives and what it can do.
Context: The Handler's Lifeline
A handler doesn't execute in a vacuum. It needs to know:
- What document is currently open?
- What's selected in the table?
- What workspace are we in?
- What's the user's permission level?
- What AI model are they using?
We manage this through the ContextManager (stored in lib/intelligence/contextManager.tsx). Every intent handler receives a context object that looks roughly like this:
interface ExecutionContext {
user: User;
workspace: Workspace;
activeDocument?: Document;
selectedDocuments: Document[];
cursorPosition?: number;
aiConfig: AIConfig;
permissions: Permission[];
timestamp: Date;
}
The context is built once when the user opens Universal Command, and handlers can query it without re-fetching. This is critical for performance—if every handler had to fetch user permissions, workspace data, and document metadata, the whole system would crawl.
50+ Handlers: The Taxonomy
We organize intents into five categories:
Document Operations (15 handlers)
- Create, duplicate, delete, rename, archive
- Move, copy, share, change permissions
- Pin, unpin, favorite, unfavorite
- Export (PDF, Word, Markdown, etc.)
Content Manipulation (12 handlers)
- Summarize, extract key points, generate outline
- Rewrite for clarity, expand, condense
- Change tone (formal, casual, technical)
- Generate table of contents
Search & Navigation (10 handlers)
- Search by keyword, filter by tag, filter by date
- Find documents by owner, by size, by type
- Jump to workspace, jump to team
- Go to settings, go to dashboard
Collaboration (8 handlers)
- Share with specific user, share with team
- Request feedback, assign task
- Add comment, mention user
- Change document permissions
Workspace Management (5 handlers)
- Create workspace, rename workspace, delete workspace
- Invite user, remove user
- Change workspace settings, export workspace data
Each handler is a pure function that takes context and parameters, validates them, and returns a result. No side effects. No global state mutations. This makes them testable and composable.
The Execution Flow: From Command to Result
Here's what happens when you type "summarize this document" and hit Enter:
-
Input Capture (1ms)
- User types in the command input field
- Text is captured in real-time
-
Intent Detection (5-50ms)
- Pattern matching identifies
SUMMARIZEintent - Parameters extracted:
{ target: 'document' }
- Pattern matching identifies
-
Context Building (10-30ms)
ContextManagergathers current document, user, workspace- Permissions are checked
-
Handler Lookup (1ms)
- Router finds
handleSummarizein the registry
- Router finds
-
Validation (5-20ms)
- Handler checks: Is there an active document? Does user have permission? Is the document large enough to summarize?
- If validation fails, return a helpful error message
-
Execution (100-5000ms depending on handler)
- For
SUMMARIZE: Claude processes the document - Result is streamed back to the UI
- User sees the summary appear in real-time
- For
-
Logging & Analytics (5ms)
- We log the intent, parameters, and result
- This data feeds our performance monitoring and helps us identify which intents users actually use
The entire pipeline is instrumented with timing data. We know which handlers are slow, which fail most often, and which are never used.
Why This Architecture Scales
Here's why this design handles 50+ intents without collapsing:
Decoupling: Each handler is independent. Adding a new intent doesn't require touching the router, the context manager, or any other handler. You write a function and register it.
Testability: Because handlers are pure functions, you can test them in isolation. No mocking complex UI state. No integration test hell.
Performance: The two-stage detection pipeline means common queries are fast. Context is built once and cached. Handlers run in parallel where possible (e.g., fetching document metadata while the AI is processing).
Observability: Every intent execution is logged with timing data. We can see which handlers are bottlenecks, which fail silently, which users never discover.
Extensibility: Adding a new intent is literally three steps:
- Write a handler function
- Register it in the
intentHandlersmap - Add a pattern or update the semantic prompt
No refactoring. No risk of breaking existing intents.
What This Means For You
If you use Universal Command regularly, this architecture is why it feels responsive even when you're executing complex operations. It's why you can chain commands together without waiting for each one to complete. It's why adding new AI-powered features doesn't slow down the existing ones.
More importantly, it's why Universal Command will keep getting better without becoming slower or more fragile. We can add 10 more intents next month without touching the core system. We can optimize individual handlers without affecting others.
The next time you press Ctrl+Shift+A and type something you've never tried before, and it just works—that's the result of treating intent as a first-class concept, not a UI layer on top of buttons.
Enjoyed this article?
Get more articles like this delivered to your inbox. No spam, unsubscribe anytime.


