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}