intent_engine/
cli.rs

1use clap::{Parser, Subcommand};
2
3#[derive(Parser)]
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    #[command(subcommand)]
9    pub command: Commands,
10}
11
12#[derive(Subcommand)]
13pub enum Commands {
14    /// Task management commands
15    #[command(subcommand)]
16    Task(TaskCommands),
17
18    /// Workspace state management
19    Current {
20        /// Set the current task ID (for backward compatibility)
21        #[arg(long)]
22        set: Option<i64>,
23
24        #[command(subcommand)]
25        command: Option<CurrentAction>,
26    },
27
28    /// Generate analysis and reports
29    Report {
30        /// Time duration (e.g., "7d", "2h", "30m")
31        #[arg(long)]
32        since: Option<String>,
33
34        /// Filter by status
35        #[arg(long)]
36        status: Option<String>,
37
38        /// Filter by name pattern (FTS5)
39        #[arg(long)]
40        filter_name: Option<String>,
41
42        /// Filter by spec pattern (FTS5)
43        #[arg(long)]
44        filter_spec: Option<String>,
45
46        /// Output format
47        #[arg(long, default_value = "json")]
48        format: String,
49
50        /// Return summary only
51        #[arg(long)]
52        summary_only: bool,
53    },
54
55    /// Event logging commands
56    #[command(subcommand)]
57    Event(EventCommands),
58
59    /// Unified search across tasks and events
60    Search {
61        /// Search query (supports FTS5 syntax like "JWT AND authentication")
62        query: String,
63
64        /// Search in tasks (default: true)
65        #[arg(long, default_value = "true")]
66        tasks: bool,
67
68        /// Search in events (default: true)
69        #[arg(long, default_value = "true")]
70        events: bool,
71
72        /// Maximum number of results (default: 20)
73        #[arg(long)]
74        limit: Option<i64>,
75    },
76
77    /// Check system health and dependencies
78    Doctor,
79
80    /// Start MCP server for AI assistants (JSON-RPC stdio)
81    #[command(name = "mcp-server")]
82    McpServer,
83
84    /// Restore session context for AI agents (Focus Restoration - Phase 1)
85    ///
86    /// This command returns all context needed to restore work continuity:
87    /// - Current focused task
88    /// - Parent task and siblings progress
89    /// - Child tasks status
90    /// - Recent events (decisions, blockers, notes)
91    /// - Suggested next commands
92    ///
93    /// Designed for SessionStart hooks to inject context at the beginning of new sessions.
94    #[command(name = "session-restore")]
95    SessionRestore {
96        /// Number of recent events to include (default: 3)
97        #[arg(long, default_value = "3")]
98        include_events: usize,
99
100        /// Workspace path (default: current directory)
101        #[arg(long)]
102        workspace: Option<String>,
103    },
104
105    /// Dashboard management commands
106    #[command(subcommand)]
107    Dashboard(DashboardCommands),
108
109    /// Unified setup command for AI tool integrations
110    ///
111    /// This command provides a unified interface for setting up intent-engine integration
112    /// with various AI assistant tools. It handles both hook installation and MCP server
113    /// configuration in one step.
114    ///
115    /// Features:
116    /// - User-level or project-level installation
117    /// - Atomic setup with rollback on failure
118    /// - Built-in connectivity testing
119    /// - Diagnosis mode for troubleshooting
120    Setup {
121        /// Target tool to configure (claude-code, gemini-cli, codex)
122        #[arg(long)]
123        target: Option<String>,
124
125        /// Installation scope: user (default, recommended), project, or both
126        #[arg(long, default_value = "user")]
127        scope: String,
128
129        /// Show what would be done without actually doing it
130        #[arg(long)]
131        dry_run: bool,
132
133        /// Overwrite existing configuration
134        #[arg(long)]
135        force: bool,
136
137        /// Run diagnosis on existing setup instead of installing
138        #[arg(long)]
139        diagnose: bool,
140
141        /// Custom config file path (advanced)
142        #[arg(long)]
143        config_path: Option<String>,
144
145        /// Project directory for INTENT_ENGINE_PROJECT_DIR env var
146        #[arg(long)]
147        project_dir: Option<String>,
148    },
149
150    // ========================================
151    // Hybrid Commands - High-Frequency Aliases
152    // ========================================
153    // These are convenience aliases for the most common operations.
154    // They provide a verb-centric, streamlined UX for frequent actions.
155    // See: docs/architecture-03-cli-hybrid-command.md
156    /// Add a new task (alias for 'task add')
157    #[command(alias = "a")]
158    Add {
159        /// Task name
160        name: String,
161
162        /// Detailed specification (markdown)
163        #[arg(long)]
164        spec: Option<String>,
165
166        /// Parent task ID
167        #[arg(long)]
168        parent: Option<i64>,
169
170        /// Priority (critical, high, medium, low)
171        #[arg(long)]
172        priority: Option<String>,
173    },
174
175    /// Start a task and set focus (alias for 'task start')
176    #[command(alias = "s")]
177    Start {
178        /// Task ID
179        id: i64,
180
181        /// Include events summary
182        #[arg(long, default_value = "true")]
183        with_events: bool,
184    },
185
186    /// Complete the current focused task (alias for 'task done')
187    #[command(alias = "d")]
188    Done,
189
190    /// Switch to a different task (alias for 'task switch')
191    #[command(alias = "sw")]
192    Switch {
193        /// Task ID
194        id: i64,
195
196        /// Include events summary
197        #[arg(long)]
198        with_events: bool,
199    },
200
201    /// Record an event for current task (alias for 'event add')
202    Log {
203        /// Event type (decision, blocker, milestone, note)
204        #[arg(value_parser = ["decision", "blocker", "milestone", "note"])]
205        event_type: String,
206
207        /// Event data/description
208        data: String,
209
210        /// Task ID (optional, uses current task if not specified)
211        #[arg(long)]
212        task_id: Option<i64>,
213    },
214
215    /// Get the next recommended task (alias for 'task pick-next')
216    #[command(alias = "n")]
217    Next {
218        /// Output format (text or json)
219        #[arg(long, default_value = "text")]
220        format: String,
221    },
222
223    /// List tasks with filters (alias for 'task list')
224    ///
225    /// Examples:
226    ///   ie ls              # List all tasks
227    ///   ie ls todo         # List todo tasks
228    ///   ie ls doing        # List doing tasks
229    ///   ie ls done         # List done tasks
230    #[command(alias = "ls")]
231    List {
232        /// Filter by status (todo, doing, done)
233        status: Option<String>,
234
235        /// Filter by parent ID (use "null" for no parent)
236        #[arg(long)]
237        parent: Option<String>,
238    },
239
240    /// Get task context (alias for 'task context')
241    #[command(alias = "ctx")]
242    Context {
243        /// Task ID (optional, uses current task if omitted)
244        task_id: Option<i64>,
245    },
246
247    /// Get task details (alias for 'task get')
248    Get {
249        /// Task ID
250        id: i64,
251
252        /// Include events summary
253        #[arg(long)]
254        with_events: bool,
255    },
256}
257
258#[derive(Subcommand)]
259pub enum CurrentAction {
260    /// Set the current task (low-level atomic command, prefer 'ie task start')
261    #[command(hide = true)]
262    Set {
263        /// Task ID to set as current
264        task_id: i64,
265    },
266
267    /// Clear the current task (low-level atomic command, prefer 'ie task done')
268    #[command(hide = true)]
269    Clear,
270}
271
272#[derive(Subcommand)]
273pub enum TaskCommands {
274    /// Add a new task
275    Add {
276        /// Task name
277        #[arg(long)]
278        name: String,
279
280        /// Parent task ID
281        #[arg(long)]
282        parent: Option<i64>,
283
284        /// Read spec from stdin
285        #[arg(long)]
286        spec_stdin: bool,
287    },
288
289    /// Get a task by ID
290    Get {
291        /// Task ID
292        id: i64,
293
294        /// Include events summary
295        #[arg(long)]
296        with_events: bool,
297    },
298
299    /// Update a task
300    Update {
301        /// Task ID
302        id: i64,
303
304        /// New task name
305        #[arg(long)]
306        name: Option<String>,
307
308        /// New parent task ID
309        #[arg(long)]
310        parent: Option<i64>,
311
312        /// New status
313        #[arg(long)]
314        status: Option<String>,
315
316        /// Task complexity (1-10)
317        #[arg(long)]
318        complexity: Option<i32>,
319
320        /// Task priority (critical, high, medium, low)
321        #[arg(long)]
322        priority: Option<String>,
323
324        /// Read spec from stdin
325        #[arg(long)]
326        spec_stdin: bool,
327    },
328
329    /// Delete a task
330    Del {
331        /// Task ID
332        id: i64,
333    },
334
335    /// List tasks with filters
336    ///
337    /// Examples:
338    ///   ie task list              # List all tasks
339    ///   ie task list todo         # List todo tasks
340    ///   ie task list doing        # List doing tasks
341    ///   ie task list done         # List done tasks
342    List {
343        /// Filter by status (todo, doing, done)
344        status: Option<String>,
345
346        /// Filter by parent ID (use "null" for no parent)
347        #[arg(long)]
348        parent: Option<String>,
349    },
350
351    /// Start a task (atomic: update status + set current)
352    Start {
353        /// Task ID
354        id: i64,
355
356        /// Include events summary
357        #[arg(long)]
358        with_events: bool,
359    },
360
361    /// Complete the current focused task (atomic: check children + update status + clear current)
362    /// This command only operates on the current_task_id. It will:
363    /// - Check all subtasks are done
364    /// - Update the task status to done
365    /// - Clear the current_task_id
366    ///
367    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
368    Done,
369
370    /// Intelligently recommend the next task to work on
371    ///
372    /// This command uses a context-aware priority model to recommend a single task:
373    /// 1. First priority: Subtasks of the current focused task (depth-first)
374    /// 2. Second priority: Top-level tasks (breadth-first)
375    ///
376    /// The command is non-interactive and does not modify task status.
377    PickNext {
378        /// Output format (text or json)
379        #[arg(long, default_value = "text")]
380        format: String,
381    },
382
383    /// Create a subtask under current task and switch to it
384    SpawnSubtask {
385        /// Subtask name
386        #[arg(long)]
387        name: String,
388
389        /// Read spec from stdin
390        #[arg(long)]
391        spec_stdin: bool,
392    },
393
394    /// Switch to a specific task (atomic: update to doing + set current)
395    Switch {
396        /// Task ID
397        id: i64,
398    },
399
400    /// Add a dependency between tasks
401    ///
402    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
403    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
404    ///
405    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
406    /// (Task 41 must be done before Task 42 can start)
407    #[command(name = "depends-on")]
408    DependsOn {
409        /// Task ID that has the dependency (blocked task)
410        blocked_task_id: i64,
411
412        /// Task ID that must be completed first (blocking task)
413        blocking_task_id: i64,
414    },
415
416    /// Get task context (ancestors, siblings, children)
417    ///
418    /// Shows the full family tree of a task to understand its strategic context.
419    /// If no task ID is provided, uses the current focused task.
420    Context {
421        /// Task ID (optional, uses current task if omitted)
422        task_id: Option<i64>,
423    },
424}
425
426#[derive(Subcommand)]
427pub enum EventCommands {
428    /// Add a new event
429    Add {
430        /// Task ID (optional, uses current task if not specified)
431        #[arg(long)]
432        task_id: Option<i64>,
433
434        /// Log type
435        #[arg(long = "type")]
436        log_type: String,
437
438        /// Read discussion data from stdin
439        #[arg(long)]
440        data_stdin: bool,
441    },
442
443    /// List events for a task (or globally if task_id not specified)
444    List {
445        /// Task ID (optional, lists all events if not specified)
446        #[arg(long)]
447        task_id: Option<i64>,
448
449        /// Maximum number of events to return (default: 50)
450        #[arg(long)]
451        limit: Option<i64>,
452
453        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
454        #[arg(long = "type")]
455        log_type: Option<String>,
456
457        /// Filter events created within duration (e.g., "7d", "24h", "30m")
458        #[arg(long)]
459        since: Option<String>,
460    },
461}
462
463#[derive(Subcommand)]
464pub enum DashboardCommands {
465    /// Start the Dashboard web server for current project
466    Start {
467        /// Custom port (default: auto-allocated 3030-3099)
468        #[arg(long)]
469        port: Option<u16>,
470
471        /// Run in foreground (default: daemon mode)
472        #[arg(long)]
473        foreground: bool,
474
475        /// Don't automatically open browser
476        #[arg(long)]
477        no_browser: bool,
478    },
479
480    /// Stop the Dashboard server
481    Stop {
482        /// Stop all Dashboard instances
483        #[arg(long)]
484        all: bool,
485    },
486
487    /// Show Dashboard status
488    Status {
489        /// Show status for all projects
490        #[arg(long)]
491        all: bool,
492    },
493
494    /// List all registered Dashboard instances
495    List,
496
497    /// Open Dashboard in browser
498    Open,
499}