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