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        /// Project directory for INTENT_ENGINE_PROJECT_DIR env var
198        #[arg(long)]
199        project_dir: Option<String>,
200    },
201
202    // ========================================
203    // Hybrid Commands - High-Frequency Aliases
204    // ========================================
205    // These are convenience aliases for the most common operations.
206    // They provide a verb-centric, streamlined UX for frequent actions.
207    // See: docs/architecture-03-cli-hybrid-command.md
208    /// Add a new task (alias for 'task add')
209    #[command(alias = "a")]
210    Add {
211        /// Task name
212        name: String,
213
214        /// Detailed specification (markdown)
215        #[arg(long)]
216        spec: Option<String>,
217
218        /// Parent task ID
219        #[arg(long)]
220        parent: Option<i64>,
221
222        /// Priority (critical, high, medium, low)
223        #[arg(long)]
224        priority: Option<String>,
225    },
226
227    /// Start a task and set focus (alias for 'task start')
228    #[command(alias = "s")]
229    Start {
230        /// Task ID
231        id: i64,
232
233        /// Include events summary
234        #[arg(long, default_value = "true")]
235        with_events: bool,
236    },
237
238    /// Complete the current focused task (alias for 'task done')
239    #[command(alias = "d")]
240    Done,
241
242    /// Record an event for current task (alias for 'event add')
243    Log {
244        /// Event type (decision, blocker, milestone, note)
245        #[arg(value_parser = ["decision", "blocker", "milestone", "note"])]
246        event_type: String,
247
248        /// Event data/description
249        data: String,
250
251        /// Task ID (optional, uses current task if not specified)
252        #[arg(long)]
253        task_id: Option<i64>,
254    },
255
256    /// Get the next recommended task (alias for 'task pick-next')
257    #[command(alias = "n")]
258    Next {
259        /// Output format (text or json)
260        #[arg(long, default_value = "text")]
261        format: String,
262    },
263
264    /// List tasks with filters (alias for 'task list')
265    ///
266    /// Examples:
267    ///   ie ls              # List all tasks
268    ///   ie ls todo         # List todo tasks
269    ///   ie ls doing        # List doing tasks
270    ///   ie ls done         # List done tasks
271    #[command(alias = "ls")]
272    List {
273        /// Filter by status (todo, doing, done)
274        status: Option<String>,
275
276        /// Filter by parent ID (use "null" for no parent)
277        #[arg(long)]
278        parent: Option<String>,
279    },
280
281    /// Get task context (alias for 'task context')
282    #[command(alias = "ctx")]
283    Context {
284        /// Task ID (optional, uses current task if omitted)
285        task_id: Option<i64>,
286    },
287
288    /// Get task details (alias for 'task get')
289    Get {
290        /// Task ID
291        id: i64,
292
293        /// Include events summary
294        #[arg(long)]
295        with_events: bool,
296    },
297
298    /// Query and view application logs
299    ///
300    /// Examples:
301    ///   ie logs                           # Show recent logs from all modes
302    ///   ie logs --mode dashboard          # Show dashboard logs only
303    ///   ie logs --level error --since 1h  # Show errors from last hour
304    ///   ie logs --follow                  # Real-time log monitoring
305    Logs {
306        /// Filter by application mode (dashboard, mcp-server, cli)
307        #[arg(long)]
308        mode: Option<String>,
309
310        /// Filter by log level (error, warn, info, debug, trace)
311        #[arg(long)]
312        level: Option<String>,
313
314        /// Show logs since duration (e.g., "1h", "24h", "7d")
315        #[arg(long)]
316        since: Option<String>,
317
318        /// Show logs until timestamp (ISO8601 format)
319        #[arg(long)]
320        until: Option<String>,
321
322        /// Maximum number of log entries to show
323        #[arg(long)]
324        limit: Option<usize>,
325
326        /// Follow logs in real-time (like tail -f)
327        #[arg(short, long)]
328        follow: bool,
329
330        /// Export format (text or json)
331        #[arg(long, default_value = "text")]
332        export: String,
333    },
334}
335
336#[derive(Subcommand, Clone)]
337pub enum CurrentAction {
338    /// Set the current task (low-level atomic command, prefer 'ie task start')
339    #[command(hide = true)]
340    Set {
341        /// Task ID to set as current
342        task_id: i64,
343    },
344
345    /// Clear the current task (low-level atomic command, prefer 'ie task done')
346    #[command(hide = true)]
347    Clear,
348}
349
350#[derive(Subcommand, Clone)]
351pub enum TaskCommands {
352    /// Add a new task
353    Add {
354        /// Task name
355        #[arg(long)]
356        name: String,
357
358        /// Parent task ID
359        #[arg(long)]
360        parent: Option<i64>,
361
362        /// Read spec from stdin
363        #[arg(long)]
364        spec_stdin: bool,
365    },
366
367    /// Get a task by ID
368    Get {
369        /// Task ID
370        id: i64,
371
372        /// Include events summary
373        #[arg(long)]
374        with_events: bool,
375    },
376
377    /// Update a task
378    Update {
379        /// Task ID
380        id: i64,
381
382        /// New task name
383        #[arg(long)]
384        name: Option<String>,
385
386        /// New parent task ID
387        #[arg(long)]
388        parent: Option<i64>,
389
390        /// New status
391        #[arg(long)]
392        status: Option<String>,
393
394        /// Task complexity (1-10)
395        #[arg(long)]
396        complexity: Option<i32>,
397
398        /// Task priority (critical, high, medium, low)
399        #[arg(long)]
400        priority: Option<String>,
401
402        /// Read spec from stdin
403        #[arg(long)]
404        spec_stdin: bool,
405    },
406
407    /// Delete a task
408    Del {
409        /// Task ID
410        id: i64,
411    },
412
413    /// List tasks with filters
414    ///
415    /// Examples:
416    ///   ie task list              # List all tasks
417    ///   ie task list todo         # List todo tasks
418    ///   ie task list doing        # List doing tasks
419    ///   ie task list done         # List done tasks
420    List {
421        /// Filter by status (todo, doing, done)
422        status: Option<String>,
423
424        /// Filter by parent ID (use "null" for no parent)
425        #[arg(long)]
426        parent: Option<String>,
427    },
428
429    /// Start a task (atomic: update status + set current)
430    Start {
431        /// Task ID
432        id: i64,
433
434        /// Include events summary
435        #[arg(long)]
436        with_events: bool,
437    },
438
439    /// Complete the current focused task (atomic: check children + update status + clear current)
440    /// This command only operates on the current_task_id. It will:
441    /// - Check all subtasks are done
442    /// - Update the task status to done
443    /// - Clear the current_task_id
444    ///
445    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
446    Done,
447
448    /// Intelligently recommend the next task to work on
449    ///
450    /// This command uses a context-aware priority model to recommend a single task:
451    /// 1. First priority: Subtasks of the current focused task (depth-first)
452    /// 2. Second priority: Top-level tasks (breadth-first)
453    ///
454    /// The command is non-interactive and does not modify task status.
455    PickNext {
456        /// Output format (text or json)
457        #[arg(long, default_value = "text")]
458        format: String,
459    },
460
461    /// Create a subtask under current task and switch to it
462    SpawnSubtask {
463        /// Subtask name
464        #[arg(long)]
465        name: String,
466
467        /// Read spec from stdin
468        #[arg(long)]
469        spec_stdin: bool,
470    },
471
472    /// Switch to a specific task (atomic: update to doing + set current)
473    /// Add a dependency between tasks
474    ///
475    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
476    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
477    ///
478    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
479    /// (Task 41 must be done before Task 42 can start)
480    #[command(name = "depends-on")]
481    DependsOn {
482        /// Task ID that has the dependency (blocked task)
483        blocked_task_id: i64,
484
485        /// Task ID that must be completed first (blocking task)
486        blocking_task_id: i64,
487    },
488
489    /// Get task context (ancestors, siblings, children)
490    ///
491    /// Shows the full family tree of a task to understand its strategic context.
492    /// If no task ID is provided, uses the current focused task.
493    Context {
494        /// Task ID (optional, uses current task if omitted)
495        task_id: Option<i64>,
496    },
497}
498
499#[derive(Subcommand, Clone)]
500pub enum EventCommands {
501    /// Add a new event
502    Add {
503        /// Task ID (optional, uses current task if not specified)
504        #[arg(long)]
505        task_id: Option<i64>,
506
507        /// Log type
508        #[arg(long = "type")]
509        log_type: String,
510
511        /// Read discussion data from stdin
512        #[arg(long)]
513        data_stdin: bool,
514    },
515
516    /// List events for a task (or globally if task_id not specified)
517    List {
518        /// Task ID (optional, lists all events if not specified)
519        #[arg(long)]
520        task_id: Option<i64>,
521
522        /// Maximum number of events to return (default: 50)
523        #[arg(long)]
524        limit: Option<i64>,
525
526        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
527        #[arg(long = "type")]
528        log_type: Option<String>,
529
530        /// Filter events created within duration (e.g., "7d", "24h", "30m")
531        #[arg(long)]
532        since: Option<String>,
533    },
534}
535
536#[derive(Subcommand, Clone)]
537pub enum DashboardCommands {
538    /// Start the Dashboard web server for current project
539    Start {
540        /// Custom port (default: 11391)
541        #[arg(long)]
542        port: Option<u16>,
543
544        /// Run in foreground (default: daemon mode)
545        #[arg(long)]
546        foreground: bool,
547
548        /// Automatically open browser (default: false, use --browser to enable)
549        #[arg(long)]
550        browser: bool,
551    },
552
553    /// Stop the Dashboard server
554    Stop {
555        /// Stop all Dashboard instances
556        #[arg(long)]
557        all: bool,
558    },
559
560    /// Show Dashboard status
561    Status {
562        /// Show status for all projects
563        #[arg(long)]
564        all: bool,
565    },
566
567    /// List all registered Dashboard instances
568    List,
569
570    /// Open Dashboard in browser
571    Open,
572}