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    /// Unified setup command for AI tool integrations
106    ///
107    /// This command provides a unified interface for setting up intent-engine integration
108    /// with various AI assistant tools. It handles both hook installation and MCP server
109    /// configuration in one step.
110    ///
111    /// Features:
112    /// - User-level or project-level installation
113    /// - Atomic setup with rollback on failure
114    /// - Built-in connectivity testing
115    /// - Diagnosis mode for troubleshooting
116    Setup {
117        /// Target tool to configure (claude-code, gemini-cli, codex)
118        #[arg(long)]
119        target: Option<String>,
120
121        /// Installation scope: user (default, recommended), project, or both
122        #[arg(long, default_value = "user")]
123        scope: String,
124
125        /// Show what would be done without actually doing it
126        #[arg(long)]
127        dry_run: bool,
128
129        /// Overwrite existing configuration
130        #[arg(long)]
131        force: bool,
132
133        /// Run diagnosis on existing setup instead of installing
134        #[arg(long)]
135        diagnose: bool,
136
137        /// Custom config file path (advanced)
138        #[arg(long)]
139        config_path: Option<String>,
140
141        /// Project directory for INTENT_ENGINE_PROJECT_DIR env var
142        #[arg(long)]
143        project_dir: Option<String>,
144    },
145
146    // ========================================
147    // Hybrid Commands - High-Frequency Aliases
148    // ========================================
149    // These are convenience aliases for the most common operations.
150    // They provide a verb-centric, streamlined UX for frequent actions.
151    // See: docs/architecture-03-cli-hybrid-command.md
152    /// Add a new task (alias for 'task add')
153    #[command(alias = "a")]
154    Add {
155        /// Task name
156        name: String,
157
158        /// Detailed specification (markdown)
159        #[arg(long)]
160        spec: Option<String>,
161
162        /// Parent task ID
163        #[arg(long)]
164        parent: Option<i64>,
165
166        /// Priority (critical, high, medium, low)
167        #[arg(long)]
168        priority: Option<String>,
169    },
170
171    /// Start a task and set focus (alias for 'task start')
172    #[command(alias = "s")]
173    Start {
174        /// Task ID
175        id: i64,
176
177        /// Include events summary
178        #[arg(long, default_value = "true")]
179        with_events: bool,
180    },
181
182    /// Complete the current focused task (alias for 'task done')
183    #[command(alias = "d")]
184    Done,
185
186    /// Switch to a different task (alias for 'task switch')
187    #[command(alias = "sw")]
188    Switch {
189        /// Task ID
190        id: i64,
191
192        /// Include events summary
193        #[arg(long)]
194        with_events: bool,
195    },
196
197    /// Record an event for current task (alias for 'event add')
198    Log {
199        /// Event type (decision, blocker, milestone, note)
200        #[arg(value_parser = ["decision", "blocker", "milestone", "note"])]
201        event_type: String,
202
203        /// Event data/description
204        data: String,
205
206        /// Task ID (optional, uses current task if not specified)
207        #[arg(long)]
208        task_id: Option<i64>,
209    },
210
211    /// Get the next recommended task (alias for 'task pick-next')
212    #[command(alias = "n")]
213    Next {
214        /// Output format (text or json)
215        #[arg(long, default_value = "text")]
216        format: String,
217    },
218
219    /// List tasks with filters (alias for 'task list')
220    ///
221    /// Examples:
222    ///   ie ls              # List all tasks
223    ///   ie ls todo         # List todo tasks
224    ///   ie ls doing        # List doing tasks
225    ///   ie ls done         # List done tasks
226    #[command(alias = "ls")]
227    List {
228        /// Filter by status (todo, doing, done)
229        status: Option<String>,
230
231        /// Filter by parent ID (use "null" for no parent)
232        #[arg(long)]
233        parent: Option<String>,
234    },
235
236    /// Get task context (alias for 'task context')
237    #[command(alias = "ctx")]
238    Context {
239        /// Task ID (optional, uses current task if omitted)
240        task_id: Option<i64>,
241    },
242
243    /// Get task details (alias for 'task get')
244    Get {
245        /// Task ID
246        id: i64,
247
248        /// Include events summary
249        #[arg(long)]
250        with_events: bool,
251    },
252}
253
254#[derive(Subcommand)]
255pub enum CurrentAction {
256    /// Set the current task (low-level atomic command, prefer 'ie task start')
257    #[command(hide = true)]
258    Set {
259        /// Task ID to set as current
260        task_id: i64,
261    },
262
263    /// Clear the current task (low-level atomic command, prefer 'ie task done')
264    #[command(hide = true)]
265    Clear,
266}
267
268#[derive(Subcommand)]
269pub enum TaskCommands {
270    /// Add a new task
271    Add {
272        /// Task name
273        #[arg(long)]
274        name: String,
275
276        /// Parent task ID
277        #[arg(long)]
278        parent: Option<i64>,
279
280        /// Read spec from stdin
281        #[arg(long)]
282        spec_stdin: bool,
283    },
284
285    /// Get a task by ID
286    Get {
287        /// Task ID
288        id: i64,
289
290        /// Include events summary
291        #[arg(long)]
292        with_events: bool,
293    },
294
295    /// Update a task
296    Update {
297        /// Task ID
298        id: i64,
299
300        /// New task name
301        #[arg(long)]
302        name: Option<String>,
303
304        /// New parent task ID
305        #[arg(long)]
306        parent: Option<i64>,
307
308        /// New status
309        #[arg(long)]
310        status: Option<String>,
311
312        /// Task complexity (1-10)
313        #[arg(long)]
314        complexity: Option<i32>,
315
316        /// Task priority (critical, high, medium, low)
317        #[arg(long)]
318        priority: Option<String>,
319
320        /// Read spec from stdin
321        #[arg(long)]
322        spec_stdin: bool,
323    },
324
325    /// Delete a task
326    Del {
327        /// Task ID
328        id: i64,
329    },
330
331    /// List tasks with filters
332    ///
333    /// Examples:
334    ///   ie task list              # List all tasks
335    ///   ie task list todo         # List todo tasks
336    ///   ie task list doing        # List doing tasks
337    ///   ie task list done         # List done tasks
338    List {
339        /// Filter by status (todo, doing, done)
340        status: Option<String>,
341
342        /// Filter by parent ID (use "null" for no parent)
343        #[arg(long)]
344        parent: Option<String>,
345    },
346
347    /// Start a task (atomic: update status + set current)
348    Start {
349        /// Task ID
350        id: i64,
351
352        /// Include events summary
353        #[arg(long)]
354        with_events: bool,
355    },
356
357    /// Complete the current focused task (atomic: check children + update status + clear current)
358    /// This command only operates on the current_task_id. It will:
359    /// - Check all subtasks are done
360    /// - Update the task status to done
361    /// - Clear the current_task_id
362    ///
363    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
364    Done,
365
366    /// Intelligently recommend the next task to work on
367    ///
368    /// This command uses a context-aware priority model to recommend a single task:
369    /// 1. First priority: Subtasks of the current focused task (depth-first)
370    /// 2. Second priority: Top-level tasks (breadth-first)
371    ///
372    /// The command is non-interactive and does not modify task status.
373    PickNext {
374        /// Output format (text or json)
375        #[arg(long, default_value = "text")]
376        format: String,
377    },
378
379    /// Create a subtask under current task and switch to it
380    SpawnSubtask {
381        /// Subtask name
382        #[arg(long)]
383        name: String,
384
385        /// Read spec from stdin
386        #[arg(long)]
387        spec_stdin: bool,
388    },
389
390    /// Switch to a specific task (atomic: update to doing + set current)
391    Switch {
392        /// Task ID
393        id: i64,
394    },
395
396    /// Add a dependency between tasks
397    ///
398    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
399    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
400    ///
401    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
402    /// (Task 41 must be done before Task 42 can start)
403    #[command(name = "depends-on")]
404    DependsOn {
405        /// Task ID that has the dependency (blocked task)
406        blocked_task_id: i64,
407
408        /// Task ID that must be completed first (blocking task)
409        blocking_task_id: i64,
410    },
411
412    /// Get task context (ancestors, siblings, children)
413    ///
414    /// Shows the full family tree of a task to understand its strategic context.
415    /// If no task ID is provided, uses the current focused task.
416    Context {
417        /// Task ID (optional, uses current task if omitted)
418        task_id: Option<i64>,
419    },
420}
421
422#[derive(Subcommand)]
423pub enum EventCommands {
424    /// Add a new event
425    Add {
426        /// Task ID (optional, uses current task if not specified)
427        #[arg(long)]
428        task_id: Option<i64>,
429
430        /// Log type
431        #[arg(long = "type")]
432        log_type: String,
433
434        /// Read discussion data from stdin
435        #[arg(long)]
436        data_stdin: bool,
437    },
438
439    /// List events for a task (or globally if task_id not specified)
440    List {
441        /// Task ID (optional, lists all events if not specified)
442        #[arg(long)]
443        task_id: Option<i64>,
444
445        /// Maximum number of events to return (default: 50)
446        #[arg(long)]
447        limit: Option<i64>,
448
449        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
450        #[arg(long = "type")]
451        log_type: Option<String>,
452
453        /// Filter events created within duration (e.g., "7d", "24h", "30m")
454        #[arg(long)]
455        since: Option<String>,
456    },
457}