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