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