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}