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}