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
276#[derive(Subcommand, Clone)]
277pub enum CurrentAction {
278    /// Set the current task (low-level atomic command, prefer 'ie task start')
279    #[command(hide = true)]
280    Set {
281        /// Task ID to set as current
282        task_id: i64,
283    },
284
285    /// Clear the current task (low-level atomic command, prefer 'ie task done')
286    #[command(hide = true)]
287    Clear,
288}
289
290#[derive(Subcommand, Clone)]
291pub enum TaskCommands {
292    /// Add a new task
293    Add {
294        /// Task name
295        #[arg(long)]
296        name: String,
297
298        /// Parent task ID
299        #[arg(long)]
300        parent: Option<i64>,
301
302        /// Read spec from stdin
303        #[arg(long)]
304        spec_stdin: bool,
305    },
306
307    /// Get a task by ID
308    Get {
309        /// Task ID
310        id: i64,
311
312        /// Include events summary
313        #[arg(long)]
314        with_events: bool,
315    },
316
317    /// Update a task
318    Update {
319        /// Task ID
320        id: i64,
321
322        /// New task name
323        #[arg(long)]
324        name: Option<String>,
325
326        /// New parent task ID
327        #[arg(long)]
328        parent: Option<i64>,
329
330        /// New status
331        #[arg(long)]
332        status: Option<String>,
333
334        /// Task complexity (1-10)
335        #[arg(long)]
336        complexity: Option<i32>,
337
338        /// Task priority (critical, high, medium, low)
339        #[arg(long)]
340        priority: Option<String>,
341
342        /// Read spec from stdin
343        #[arg(long)]
344        spec_stdin: bool,
345    },
346
347    /// Delete a task
348    Del {
349        /// Task ID
350        id: i64,
351    },
352
353    /// List tasks with filters
354    ///
355    /// Examples:
356    ///   ie task list              # List all tasks
357    ///   ie task list todo         # List todo tasks
358    ///   ie task list doing        # List doing tasks
359    ///   ie task list done         # List done tasks
360    List {
361        /// Filter by status (todo, doing, done)
362        status: Option<String>,
363
364        /// Filter by parent ID (use "null" for no parent)
365        #[arg(long)]
366        parent: Option<String>,
367    },
368
369    /// Start a task (atomic: update status + set current)
370    Start {
371        /// Task ID
372        id: i64,
373
374        /// Include events summary
375        #[arg(long)]
376        with_events: bool,
377    },
378
379    /// Complete the current focused task (atomic: check children + update status + clear current)
380    /// This command only operates on the current_task_id. It will:
381    /// - Check all subtasks are done
382    /// - Update the task status to done
383    /// - Clear the current_task_id
384    ///
385    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
386    Done,
387
388    /// Intelligently recommend the next task to work on
389    ///
390    /// This command uses a context-aware priority model to recommend a single task:
391    /// 1. First priority: Subtasks of the current focused task (depth-first)
392    /// 2. Second priority: Top-level tasks (breadth-first)
393    ///
394    /// The command is non-interactive and does not modify task status.
395    PickNext {
396        /// Output format (text or json)
397        #[arg(long, default_value = "text")]
398        format: String,
399    },
400
401    /// Create a subtask under current task and switch to it
402    SpawnSubtask {
403        /// Subtask name
404        #[arg(long)]
405        name: String,
406
407        /// Read spec from stdin
408        #[arg(long)]
409        spec_stdin: bool,
410    },
411
412    /// Switch to a specific task (atomic: update to doing + set current)
413    /// Add a dependency between tasks
414    ///
415    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
416    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
417    ///
418    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
419    /// (Task 41 must be done before Task 42 can start)
420    #[command(name = "depends-on")]
421    DependsOn {
422        /// Task ID that has the dependency (blocked task)
423        blocked_task_id: i64,
424
425        /// Task ID that must be completed first (blocking task)
426        blocking_task_id: i64,
427    },
428
429    /// Get task context (ancestors, siblings, children)
430    ///
431    /// Shows the full family tree of a task to understand its strategic context.
432    /// If no task ID is provided, uses the current focused task.
433    Context {
434        /// Task ID (optional, uses current task if omitted)
435        task_id: Option<i64>,
436    },
437}
438
439#[derive(Subcommand, Clone)]
440pub enum EventCommands {
441    /// Add a new event
442    Add {
443        /// Task ID (optional, uses current task if not specified)
444        #[arg(long)]
445        task_id: Option<i64>,
446
447        /// Log type
448        #[arg(long = "type")]
449        log_type: String,
450
451        /// Read discussion data from stdin
452        #[arg(long)]
453        data_stdin: bool,
454    },
455
456    /// List events for a task (or globally if task_id not specified)
457    List {
458        /// Task ID (optional, lists all events if not specified)
459        #[arg(long)]
460        task_id: Option<i64>,
461
462        /// Maximum number of events to return (default: 50)
463        #[arg(long)]
464        limit: Option<i64>,
465
466        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
467        #[arg(long = "type")]
468        log_type: Option<String>,
469
470        /// Filter events created within duration (e.g., "7d", "24h", "30m")
471        #[arg(long)]
472        since: Option<String>,
473    },
474}
475
476#[derive(Subcommand, Clone)]
477pub enum DashboardCommands {
478    /// Start the Dashboard web server for current project
479    Start {
480        /// Custom port (default: 11391)
481        #[arg(long)]
482        port: Option<u16>,
483
484        /// Run in foreground (default: daemon mode)
485        #[arg(long)]
486        foreground: bool,
487
488        /// Automatically open browser (default: false, use --browser to enable)
489        #[arg(long)]
490        browser: bool,
491    },
492
493    /// Stop the Dashboard server
494    Stop {
495        /// Stop all Dashboard instances
496        #[arg(long)]
497        all: bool,
498    },
499
500    /// Show Dashboard status
501    Status {
502        /// Show status for all projects
503        #[arg(long)]
504        all: bool,
505    },
506
507    /// List all registered Dashboard instances
508    List,
509
510    /// Open Dashboard in browser
511    Open,
512}