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
276#[derive(Subcommand, Clone)]
277pub enum CurrentAction {
278 /// Set the current task (low-level atomic command, prefer 'ie task start')
279 #[command(hide = true)]
280 Set {
281 /// Task ID to set as current
282 task_id: i64,
283 },
284
285 /// Clear the current task (low-level atomic command, prefer 'ie task done')
286 #[command(hide = true)]
287 Clear,
288}
289
290#[derive(Subcommand, Clone)]
291pub enum TaskCommands {
292 /// Add a new task
293 Add {
294 /// Task name
295 #[arg(long)]
296 name: String,
297
298 /// Parent task ID
299 #[arg(long)]
300 parent: Option<i64>,
301
302 /// Read spec from stdin
303 #[arg(long)]
304 spec_stdin: bool,
305 },
306
307 /// Get a task by ID
308 Get {
309 /// Task ID
310 id: i64,
311
312 /// Include events summary
313 #[arg(long)]
314 with_events: bool,
315 },
316
317 /// Update a task
318 Update {
319 /// Task ID
320 id: i64,
321
322 /// New task name
323 #[arg(long)]
324 name: Option<String>,
325
326 /// New parent task ID
327 #[arg(long)]
328 parent: Option<i64>,
329
330 /// New status
331 #[arg(long)]
332 status: Option<String>,
333
334 /// Task complexity (1-10)
335 #[arg(long)]
336 complexity: Option<i32>,
337
338 /// Task priority (critical, high, medium, low)
339 #[arg(long)]
340 priority: Option<String>,
341
342 /// Read spec from stdin
343 #[arg(long)]
344 spec_stdin: bool,
345 },
346
347 /// Delete a task
348 Del {
349 /// Task ID
350 id: i64,
351 },
352
353 /// List tasks with filters
354 ///
355 /// Examples:
356 /// ie task list # List all tasks
357 /// ie task list todo # List todo tasks
358 /// ie task list doing # List doing tasks
359 /// ie task list done # List done tasks
360 List {
361 /// Filter by status (todo, doing, done)
362 status: Option<String>,
363
364 /// Filter by parent ID (use "null" for no parent)
365 #[arg(long)]
366 parent: Option<String>,
367 },
368
369 /// Start a task (atomic: update status + set current)
370 Start {
371 /// Task ID
372 id: i64,
373
374 /// Include events summary
375 #[arg(long)]
376 with_events: bool,
377 },
378
379 /// Complete the current focused task (atomic: check children + update status + clear current)
380 /// This command only operates on the current_task_id. It will:
381 /// - Check all subtasks are done
382 /// - Update the task status to done
383 /// - Clear the current_task_id
384 ///
385 /// Prerequisites: A task must be set as current (via `current --set <ID>`)
386 Done,
387
388 /// Intelligently recommend the next task to work on
389 ///
390 /// This command uses a context-aware priority model to recommend a single task:
391 /// 1. First priority: Subtasks of the current focused task (depth-first)
392 /// 2. Second priority: Top-level tasks (breadth-first)
393 ///
394 /// The command is non-interactive and does not modify task status.
395 PickNext {
396 /// Output format (text or json)
397 #[arg(long, default_value = "text")]
398 format: String,
399 },
400
401 /// Create a subtask under current task and switch to it
402 SpawnSubtask {
403 /// Subtask name
404 #[arg(long)]
405 name: String,
406
407 /// Read spec from stdin
408 #[arg(long)]
409 spec_stdin: bool,
410 },
411
412 /// Switch to a specific task (atomic: update to doing + set current)
413 /// Add a dependency between tasks
414 ///
415 /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
416 /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
417 ///
418 /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
419 /// (Task 41 must be done before Task 42 can start)
420 #[command(name = "depends-on")]
421 DependsOn {
422 /// Task ID that has the dependency (blocked task)
423 blocked_task_id: i64,
424
425 /// Task ID that must be completed first (blocking task)
426 blocking_task_id: i64,
427 },
428
429 /// Get task context (ancestors, siblings, children)
430 ///
431 /// Shows the full family tree of a task to understand its strategic context.
432 /// If no task ID is provided, uses the current focused task.
433 Context {
434 /// Task ID (optional, uses current task if omitted)
435 task_id: Option<i64>,
436 },
437}
438
439#[derive(Subcommand, Clone)]
440pub enum EventCommands {
441 /// Add a new event
442 Add {
443 /// Task ID (optional, uses current task if not specified)
444 #[arg(long)]
445 task_id: Option<i64>,
446
447 /// Log type
448 #[arg(long = "type")]
449 log_type: String,
450
451 /// Read discussion data from stdin
452 #[arg(long)]
453 data_stdin: bool,
454 },
455
456 /// List events for a task (or globally if task_id not specified)
457 List {
458 /// Task ID (optional, lists all events if not specified)
459 #[arg(long)]
460 task_id: Option<i64>,
461
462 /// Maximum number of events to return (default: 50)
463 #[arg(long)]
464 limit: Option<i64>,
465
466 /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
467 #[arg(long = "type")]
468 log_type: Option<String>,
469
470 /// Filter events created within duration (e.g., "7d", "24h", "30m")
471 #[arg(long)]
472 since: Option<String>,
473 },
474}
475
476#[derive(Subcommand, Clone)]
477pub enum DashboardCommands {
478 /// Start the Dashboard web server for current project
479 Start {
480 /// Custom port (default: 11391)
481 #[arg(long)]
482 port: Option<u16>,
483
484 /// Run in foreground (default: daemon mode)
485 #[arg(long)]
486 foreground: bool,
487
488 /// Automatically open browser (default: false, use --browser to enable)
489 #[arg(long)]
490 browser: bool,
491 },
492
493 /// Stop the Dashboard server
494 Stop {
495 /// Stop all Dashboard instances
496 #[arg(long)]
497 all: bool,
498 },
499
500 /// Show Dashboard status
501 Status {
502 /// Show status for all projects
503 #[arg(long)]
504 all: bool,
505 },
506
507 /// List all registered Dashboard instances
508 List,
509
510 /// Open Dashboard in browser
511 Open,
512}