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
21        #[arg(long)]
22        set: Option<i64>,
23    },
24
25    /// Generate analysis and reports
26    Report {
27        /// Time duration (e.g., "7d", "2h", "30m")
28        #[arg(long)]
29        since: Option<String>,
30
31        /// Filter by status
32        #[arg(long)]
33        status: Option<String>,
34
35        /// Filter by name pattern (FTS5)
36        #[arg(long)]
37        filter_name: Option<String>,
38
39        /// Filter by spec pattern (FTS5)
40        #[arg(long)]
41        filter_spec: Option<String>,
42
43        /// Output format
44        #[arg(long, default_value = "json")]
45        format: String,
46
47        /// Return summary only
48        #[arg(long)]
49        summary_only: bool,
50    },
51
52    /// Event logging commands
53    #[command(subcommand)]
54    Event(EventCommands),
55
56    /// Check system health and dependencies
57    Doctor,
58
59    /// Start MCP server for AI assistants (JSON-RPC stdio)
60    #[command(name = "mcp-server")]
61    McpServer,
62
63    /// Restore session context for AI agents (Focus Restoration - Phase 1)
64    ///
65    /// This command returns all context needed to restore work continuity:
66    /// - Current focused task
67    /// - Parent task and siblings progress
68    /// - Child tasks status
69    /// - Recent events (decisions, blockers, notes)
70    /// - Suggested next commands
71    ///
72    /// Designed for SessionStart hooks to inject context at the beginning of new sessions.
73    #[command(name = "session-restore")]
74    SessionRestore {
75        /// Number of recent events to include (default: 3)
76        #[arg(long, default_value = "3")]
77        include_events: usize,
78
79        /// Workspace path (default: current directory)
80        #[arg(long)]
81        workspace: Option<String>,
82    },
83
84    /// Setup Claude Code integration (install SessionStart hook)
85    ///
86    /// Automatically configures .claude/hooks/session-start.sh to enable
87    /// focus restoration on every new Claude Code session.
88    #[command(name = "setup-claude-code")]
89    SetupClaudeCode {
90        /// Show what would be done without actually doing it
91        #[arg(long)]
92        dry_run: bool,
93
94        /// Specify .claude directory location (default: ./.claude)
95        #[arg(long)]
96        claude_dir: Option<String>,
97
98        /// Overwrite existing hook
99        #[arg(long)]
100        force: bool,
101    },
102
103    /// Setup MCP server configuration for Claude Code/Desktop
104    ///
105    /// Automatically configures ~/.claude.json (or OS-specific equivalent)
106    /// to add intent-engine as an MCP server. This allows Claude Code/Desktop
107    /// to use intent-engine tools for task management.
108    ///
109    /// Run this after 'cargo install intent-engine' to complete the setup.
110    #[command(name = "setup-mcp")]
111    SetupMcp {
112        /// Show what would be done without actually doing it
113        #[arg(long)]
114        dry_run: bool,
115
116        /// Custom config file path (default: auto-detect)
117        #[arg(long)]
118        config_path: Option<String>,
119
120        /// Project directory for INTENT_ENGINE_PROJECT_DIR env var
121        #[arg(long)]
122        project_dir: Option<String>,
123
124        /// Overwrite existing configuration
125        #[arg(long)]
126        force: bool,
127
128        /// Target application: claude-code or claude-desktop
129        #[arg(long, default_value = "claude-code")]
130        target: String,
131    },
132}
133
134#[derive(Subcommand)]
135pub enum TaskCommands {
136    /// Add a new task
137    Add {
138        /// Task name
139        #[arg(long)]
140        name: String,
141
142        /// Parent task ID
143        #[arg(long)]
144        parent: Option<i64>,
145
146        /// Read spec from stdin
147        #[arg(long)]
148        spec_stdin: bool,
149    },
150
151    /// Get a task by ID
152    Get {
153        /// Task ID
154        id: i64,
155
156        /// Include events summary
157        #[arg(long)]
158        with_events: bool,
159    },
160
161    /// Update a task
162    Update {
163        /// Task ID
164        id: i64,
165
166        /// New task name
167        #[arg(long)]
168        name: Option<String>,
169
170        /// New parent task ID
171        #[arg(long)]
172        parent: Option<i64>,
173
174        /// New status
175        #[arg(long)]
176        status: Option<String>,
177
178        /// Task complexity (1-10)
179        #[arg(long)]
180        complexity: Option<i32>,
181
182        /// Task priority (critical, high, medium, low)
183        #[arg(long)]
184        priority: Option<String>,
185
186        /// Read spec from stdin
187        #[arg(long)]
188        spec_stdin: bool,
189    },
190
191    /// Delete a task
192    Del {
193        /// Task ID
194        id: i64,
195    },
196
197    /// List tasks with filters
198    List {
199        /// Filter by status
200        #[arg(long)]
201        status: Option<String>,
202
203        /// Filter by parent ID (use "null" for no parent)
204        #[arg(long)]
205        parent: Option<String>,
206    },
207
208    /// Find tasks with filters (deprecated: use 'list' instead)
209    #[command(hide = true)]
210    Find {
211        /// Filter by status
212        #[arg(long)]
213        status: Option<String>,
214
215        /// Filter by parent ID (use "null" for no parent)
216        #[arg(long)]
217        parent: Option<String>,
218    },
219
220    /// Start a task (atomic: update status + set current)
221    Start {
222        /// Task ID
223        id: i64,
224
225        /// Include events summary
226        #[arg(long)]
227        with_events: bool,
228    },
229
230    /// Complete the current focused task (atomic: check children + update status + clear current)
231    /// This command only operates on the current_task_id. It will:
232    /// - Check all subtasks are done
233    /// - Update the task status to done
234    /// - Clear the current_task_id
235    ///
236    ///   Prerequisites: A task must be set as current (via `current --set <ID>`)
237    Done,
238
239    /// Intelligently recommend the next task to work on
240    ///
241    /// This command uses a context-aware priority model to recommend a single task:
242    /// 1. First priority: Subtasks of the current focused task (depth-first)
243    /// 2. Second priority: Top-level tasks (breadth-first)
244    ///
245    /// The command is non-interactive and does not modify task status.
246    PickNext {
247        /// Output format (text or json)
248        #[arg(long, default_value = "text")]
249        format: String,
250    },
251
252    /// Create a subtask under current task and switch to it
253    SpawnSubtask {
254        /// Subtask name
255        #[arg(long)]
256        name: String,
257
258        /// Read spec from stdin
259        #[arg(long)]
260        spec_stdin: bool,
261    },
262
263    /// Switch to a specific task (atomic: update to doing + set current)
264    Switch {
265        /// Task ID
266        id: i64,
267    },
268
269    /// Search tasks by content using full-text search
270    Search {
271        /// Search query (supports FTS5 syntax like "bug AND NOT critical")
272        query: String,
273    },
274
275    /// Add a dependency between tasks
276    ///
277    /// Creates a dependency where BLOCKED_TASK depends on BLOCKING_TASK.
278    /// The BLOCKING_TASK must be completed before BLOCKED_TASK can be started.
279    ///
280    /// Example: `task depends-on 42 41` means Task 42 depends on Task 41
281    /// (Task 41 must be done before Task 42 can start)
282    #[command(name = "depends-on")]
283    DependsOn {
284        /// Task ID that has the dependency (blocked task)
285        blocked_task_id: i64,
286
287        /// Task ID that must be completed first (blocking task)
288        blocking_task_id: i64,
289    },
290}
291
292#[derive(Subcommand)]
293pub enum EventCommands {
294    /// Add a new event
295    Add {
296        /// Task ID (optional, uses current task if not specified)
297        #[arg(long)]
298        task_id: Option<i64>,
299
300        /// Log type
301        #[arg(long = "type")]
302        log_type: String,
303
304        /// Read discussion data from stdin
305        #[arg(long)]
306        data_stdin: bool,
307    },
308
309    /// List events for a task
310    List {
311        /// Task ID
312        #[arg(long)]
313        task_id: i64,
314
315        /// Maximum number of events to return
316        #[arg(long)]
317        limit: Option<i64>,
318
319        /// Filter by log type (e.g., "decision", "blocker", "milestone", "note")
320        #[arg(long = "type")]
321        log_type: Option<String>,
322
323        /// Filter events created within duration (e.g., "7d", "24h", "30m")
324        #[arg(long)]
325        since: Option<String>,
326    },
327}