intent_engine/
cli.rs

1use clap::{Parser, Subcommand};
2
3#[derive(Parser, Clone)]
4#[command(name = "intent-engine")]
5#[command(about = "A command-line database service for tracking strategic intent", long_about = None)]
6#[command(version)]
7pub struct Cli {
8    /// Enable verbose output (-v)
9    #[arg(short, long, action = clap::ArgAction::Count)]
10    pub verbose: u8,
11
12    /// Suppress non-error output (-q)
13    #[arg(short, long)]
14    pub quiet: bool,
15
16    /// Output logs in JSON format
17    #[arg(long)]
18    pub json: bool,
19
20    #[command(subcommand)]
21    pub command: Commands,
22}
23
24#[derive(Subcommand, Clone)]
25pub enum Commands {
26    /// Task management commands
27    #[command(subcommand)]
28    Task(TaskCommands),
29
30    /// Workspace state management
31    Current {
32        /// Set the current task ID (for backward compatibility)
33        #[arg(long)]
34        set: Option<i64>,
35
36        #[command(subcommand)]
37        command: Option<CurrentAction>,
38    },
39
40    /// Generate analysis and reports
41    Report {
42        /// Time duration (e.g., "7d", "2h", "30m")
43        #[arg(long)]
44        since: Option<String>,
45
46        /// Filter by status
47        #[arg(long)]
48        status: Option<String>,
49
50        /// Filter by name pattern (FTS5)
51        #[arg(long)]
52        filter_name: Option<String>,
53
54        /// Filter by spec pattern (FTS5)
55        #[arg(long)]
56        filter_spec: Option<String>,
57
58        /// Output format
59        #[arg(long, default_value = "json")]
60        format: String,
61
62        /// Return summary only
63        #[arg(long)]
64        summary_only: bool,
65    },
66
67    /// Create or update task structures declaratively
68    ///
69    /// Reads a JSON plan from stdin and creates/updates tasks atomically.
70    /// Supports hierarchical nesting and name-based dependencies.
71    ///
72    /// Example:
73    ///   echo '{"tasks": [{"name": "Task A", "children": [{"name": "Task B"}]}]}' | ie plan
74    Plan {
75        /// Show what would be created without actually doing it
76        #[arg(long)]
77        dry_run: bool,
78
79        /// Output format (text or json)
80        #[arg(long, default_value = "text")]
81        format: String,
82    },
83
84    /// Event logging commands
85    #[command(subcommand)]
86    Event(EventCommands),
87
88    /// Unified search across tasks and events
89    Search {
90        /// Search query (supports FTS5 syntax like "JWT AND authentication")
91        query: String,
92
93        /// Search in tasks (default: true)
94        #[arg(long, default_value = "true")]
95        tasks: bool,
96
97        /// Search in events (default: true)
98        #[arg(long, default_value = "true")]
99        events: bool,
100
101        /// Maximum number of results (default: 20)
102        #[arg(long)]
103        limit: Option<i64>,
104    },
105
106    /// Check system health and dependencies
107    Doctor,
108
109    /// Start MCP server for AI assistants (JSON-RPC stdio)
110    #[command(name = "mcp-server")]
111    McpServer,
112
113    /// Restore session context for AI agents (Focus Restoration - Phase 1)
114    ///
115    /// This command returns all context needed to restore work continuity:
116    /// - Current focused task
117    /// - Parent task and siblings progress
118    /// - Child tasks status
119    /// - Recent events (decisions, blockers, notes)
120    /// - Suggested next commands
121    ///
122    /// Designed for SessionStart hooks to inject context at the beginning of new sessions.
123    #[command(name = "session-restore")]
124    SessionRestore {
125        /// Number of recent events to include (default: 3)
126        #[arg(long, default_value = "3")]
127        include_events: usize,
128
129        /// Workspace path (default: current directory)
130        #[arg(long)]
131        workspace: Option<String>,
132    },
133
134    /// Dashboard management commands
135    #[command(subcommand)]
136    Dashboard(DashboardCommands),
137
138    /// Unified setup command for AI tool integrations
139    ///
140    /// This command provides a unified interface for setting up intent-engine integration
141    /// with various AI assistant tools. It handles both hook installation and MCP server
142    /// configuration in one step.
143    ///
144    /// Features:
145    /// - User-level or project-level installation
146    /// - Atomic setup with rollback on failure
147    /// - Built-in connectivity testing
148    /// - Diagnosis mode for troubleshooting
149    Setup {
150        /// Target tool to configure (claude-code, gemini-cli, codex)
151        #[arg(long)]
152        target: Option<String>,
153
154        /// Installation scope: user (default, recommended), project, or both
155        #[arg(long, default_value = "user")]
156        scope: String,
157
158        /// Show what would be done without actually doing it
159        #[arg(long)]
160        dry_run: bool,
161
162        /// Overwrite existing configuration
163        #[arg(long)]
164        force: bool,
165
166        /// Run diagnosis on existing setup instead of installing
167        #[arg(long)]
168        diagnose: bool,
169
170        /// Custom config file path (advanced)
171        #[arg(long)]
172        config_path: Option<String>,
173
174        /// Project directory for INTENT_ENGINE_PROJECT_DIR env var
175        #[arg(long)]
176        project_dir: Option<String>,
177    },
178
179    // ========================================
180    // Hybrid Commands - High-Frequency Aliases
181    // ========================================
182    // These are convenience aliases for the most common operations.
183    // They provide a verb-centric, streamlined UX for frequent actions.
184    // See: docs/architecture-03-cli-hybrid-command.md
185    /// Add a new task (alias for 'task add')
186    #[command(alias = "a")]
187    Add {
188        /// Task name
189        name: String,
190
191        /// Detailed specification (markdown)
192        #[arg(long)]
193        spec: Option<String>,
194
195        /// Parent task ID
196        #[arg(long)]
197        parent: Option<i64>,
198
199        /// Priority (critical, high, medium, low)
200        #[arg(long)]
201        priority: Option<String>,
202    },
203
204    /// Start a task and set focus (alias for 'task start')
205    #[command(alias = "s")]
206    Start {
207        /// Task ID
208        id: i64,
209
210        /// Include events summary
211        #[arg(long, default_value = "true")]
212        with_events: bool,
213    },
214
215    /// Complete the current focused task (alias for 'task done')
216    #[command(alias = "d")]
217    Done,
218
219    /// Switch to a different task (alias for 'task switch')
220    #[command(alias = "sw")]
221    Switch {
222        /// Task ID
223        id: i64,
224
225        /// Include events summary
226        #[arg(long)]
227        with_events: bool,
228    },
229
230    /// Record an event for current task (alias for 'event add')
231    Log {
232        /// Event type (decision, blocker, milestone, note)
233        #[arg(value_parser = ["decision", "blocker", "milestone", "note"])]
234        event_type: String,
235
236        /// Event data/description
237        data: String,
238
239        /// Task ID (optional, uses current task if not specified)
240        #[arg(long)]
241        task_id: Option<i64>,
242    },
243
244    /// Get the next recommended task (alias for 'task pick-next')
245    #[command(alias = "n")]
246    Next {
247        /// Output format (text or json)
248        #[arg(long, default_value = "text")]
249        format: String,
250    },
251
252    /// List tasks with filters (alias for 'task list')
253    ///
254    /// Examples:
255    ///   ie ls              # List all tasks
256    ///   ie ls todo         # List todo tasks
257    ///   ie ls doing        # List doing tasks
258    ///   ie ls done         # List done tasks
259    #[command(alias = "ls")]
260    List {
261        /// Filter by status (todo, doing, done)
262        status: Option<String>,
263
264        /// Filter by parent ID (use "null" for no parent)
265        #[arg(long)]
266        parent: Option<String>,
267    },
268
269    /// Get task context (alias for 'task context')
270    #[command(alias = "ctx")]
271    Context {
272        /// Task ID (optional, uses current task if omitted)
273        task_id: Option<i64>,
274    },
275
276    /// Get task details (alias for 'task get')
277    Get {
278        /// Task ID
279        id: i64,
280
281        /// Include events summary
282        #[arg(long)]
283        with_events: bool,
284    },
285}
286
287#[derive(Subcommand, Clone)]
288pub enum CurrentAction {
289    /// Set the current task (low-level atomic command, prefer 'ie task start')
290    #[command(hide = true)]
291    Set {
292        /// Task ID to set as current
293        task_id: i64,
294    },
295
296    /// Clear the current task (low-level atomic command, prefer 'ie task done')
297    #[command(hide = true)]
298    Clear,
299}
300
301#[derive(Subcommand, Clone)]
302pub enum TaskCommands {
303    /// Add a new task
304    Add {
305        /// Task name
306        #[arg(long)]
307        name: String,
308
309        /// Parent task ID
310        #[arg(long)]
311        parent: Option<i64>,
312
313        /// Read spec from stdin
314        #[arg(long)]
315        spec_stdin: bool,
316    },
317
318    /// Get a task by ID
319    Get {
320        /// Task ID
321        id: i64,
322
323        /// Include events summary
324        #[arg(long)]
325        with_events: bool,
326    },
327
328    /// Update a task
329    Update {
330        /// Task ID
331        id: i64,
332
333        /// New task name
334        #[arg(long)]
335        name: Option<String>,
336
337        /// New parent task ID
338        #[arg(long)]
339        parent: Option<i64>,
340
341        /// New status
342        #[arg(long)]
343        status: Option<String>,
344
345        /// Task complexity (1-10)
346        #[arg(long)]
347        complexity: Option<i32>,
348
349        /// Task priority (critical, high, medium, low)
350        #[arg(long)]
351        priority: Option<String>,
352
353        /// Read spec from stdin
354        #[arg(long)]
355        spec_stdin: bool,
356    },
357
358    /// Delete a task
359    Del {
360        /// Task ID
361        id: i64,
362    },
363
364    /// List tasks with filters
365    ///
366    /// Examples:
367    ///   ie task list              # List all tasks
368    ///   ie task list todo         # List todo tasks
369    ///   ie task list doing        # List doing tasks
370    ///   ie task list done         # List done tasks
371    List {
372        /// Filter by status (todo, doing, done)
373        status: Option<String>,
374
375        /// Filter by parent ID (use "null" for no parent)
376        #[arg(long)]
377        parent: Option<String>,
378    },
379
380    /// Start a task (atomic: update status + set current)
381    Start {
382        /// Task ID
383        id: i64,
384
385        /// Include events summary
386        #[arg(long)]
387        with_events: bool,
388    },
389
390    /// Complete the current focused task (atomic: check children + update status + clear current)
391    /// This command only operates on the current_task_id. It will:
392    /// - Check all subtasks are done
393    /// - Update the task status to done
394    /// - Clear the current_task_id
395    ///
396    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
397    Done,
398
399    /// Intelligently recommend the next task to work on
400    ///
401    /// This command uses a context-aware priority model to recommend a single task:
402    /// 1. First priority: Subtasks of the current focused task (depth-first)
403    /// 2. Second priority: Top-level tasks (breadth-first)
404    ///
405    /// The command is non-interactive and does not modify task status.
406    PickNext {
407        /// Output format (text or json)
408        #[arg(long, default_value = "text")]
409        format: String,
410    },
411
412    /// Create a subtask under current task and switch to it
413    SpawnSubtask {
414        /// Subtask name
415        #[arg(long)]
416        name: String,
417
418        /// Read spec from stdin
419        #[arg(long)]
420        spec_stdin: bool,
421    },
422
423    /// Switch to a specific task (atomic: update to doing + set current)
424    Switch {
425        /// Task ID
426        id: i64,
427    },
428
429    /// Add a dependency between tasks
430    ///
431    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
432    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
433    ///
434    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
435    /// (Task 41 must be done before Task 42 can start)
436    #[command(name = "depends-on")]
437    DependsOn {
438        /// Task ID that has the dependency (blocked task)
439        blocked_task_id: i64,
440
441        /// Task ID that must be completed first (blocking task)
442        blocking_task_id: i64,
443    },
444
445    /// Get task context (ancestors, siblings, children)
446    ///
447    /// Shows the full family tree of a task to understand its strategic context.
448    /// If no task ID is provided, uses the current focused task.
449    Context {
450        /// Task ID (optional, uses current task if omitted)
451        task_id: Option<i64>,
452    },
453}
454
455#[derive(Subcommand, Clone)]
456pub enum EventCommands {
457    /// Add a new event
458    Add {
459        /// Task ID (optional, uses current task if not specified)
460        #[arg(long)]
461        task_id: Option<i64>,
462
463        /// Log type
464        #[arg(long = "type")]
465        log_type: String,
466
467        /// Read discussion data from stdin
468        #[arg(long)]
469        data_stdin: bool,
470    },
471
472    /// List events for a task (or globally if task_id not specified)
473    List {
474        /// Task ID (optional, lists all events if not specified)
475        #[arg(long)]
476        task_id: Option<i64>,
477
478        /// Maximum number of events to return (default: 50)
479        #[arg(long)]
480        limit: Option<i64>,
481
482        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
483        #[arg(long = "type")]
484        log_type: Option<String>,
485
486        /// Filter events created within duration (e.g., "7d", "24h", "30m")
487        #[arg(long)]
488        since: Option<String>,
489    },
490}
491
492#[derive(Subcommand, Clone)]
493pub enum DashboardCommands {
494    /// Start the Dashboard web server for current project
495    Start {
496        /// Custom port (default: 11391)
497        #[arg(long)]
498        port: Option<u16>,
499
500        /// Run in foreground (default: daemon mode)
501        #[arg(long)]
502        foreground: bool,
503
504        /// Automatically open browser (default: false, use --browser to enable)
505        #[arg(long)]
506        browser: bool,
507    },
508
509    /// Stop the Dashboard server
510    Stop {
511        /// Stop all Dashboard instances
512        #[arg(long)]
513        all: bool,
514    },
515
516    /// Show Dashboard status
517    Status {
518        /// Show status for all projects
519        #[arg(long)]
520        all: bool,
521    },
522
523    /// List all registered Dashboard instances
524    List,
525
526    /// Open Dashboard in browser
527    Open,
528}