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