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