@stephendolan/omnifocus-cli 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -431
- package/dist/cli.js +17 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,494 +1,151 @@
|
|
|
1
1
|
# OmniFocus CLI
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@stephendolan/omnifocus-cli)
|
|
4
|
-
[](https://www.npmjs.com/package/@stephendolan/omnifocus-cli)
|
|
5
4
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://bun.sh)
|
|
7
|
-
[](https://www.apple.com/macos/)
|
|
8
5
|
|
|
9
|
-
A
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
- 📝 **Task Management** - Create, list, update, and delete tasks
|
|
14
|
-
- 📂 **Project Management** - Manage projects and their organization
|
|
15
|
-
- 📥 **Inbox Management** - View and count inbox items
|
|
16
|
-
- 🏷️ **Tag Analysis** - View tag usage statistics and identify stale tags
|
|
17
|
-
- 🔍 **Search** - Full-text search across tasks
|
|
18
|
-
- 📊 **JSON Output** - Machine-readable output for automation and scripting
|
|
19
|
-
- ⚡ **Fast** - Direct OmniFocus integration using JXA
|
|
6
|
+
A command-line interface for OmniFocus on macOS.
|
|
20
7
|
|
|
21
8
|
## Installation
|
|
22
9
|
|
|
23
|
-
Requires [Bun](https://bun.sh) runtime.
|
|
24
|
-
|
|
25
10
|
```bash
|
|
26
|
-
# Install globally with bun (recommended)
|
|
27
11
|
bun install -g @stephendolan/omnifocus-cli
|
|
28
|
-
|
|
29
|
-
# Or run directly without installing
|
|
30
|
-
bunx @stephendolan/omnifocus-cli task list
|
|
31
|
-
npx @stephendolan/omnifocus-cli task list # also works
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### From Source
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
git clone https://github.com/stephendolan/omnifocus-cli.git
|
|
38
|
-
cd omnifocus-cli
|
|
39
|
-
bun install
|
|
40
|
-
bun run link # Build and link globally
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Now you can use the `of` command anywhere in your terminal.
|
|
44
|
-
|
|
45
|
-
## Output Format
|
|
46
|
-
|
|
47
|
-
All commands output JSON by default:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# Pretty-printed JSON (default)
|
|
51
|
-
of task list
|
|
52
|
-
|
|
53
|
-
# Compact JSON (single line)
|
|
54
|
-
of task list --compact
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### Working with JSON Output
|
|
58
|
-
|
|
59
|
-
Use `jq` to filter and transform the JSON output:
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
# Count inbox tasks
|
|
63
|
-
of inbox list | jq 'length'
|
|
64
|
-
|
|
65
|
-
# Get task names only
|
|
66
|
-
of task list | jq '.[] | .name'
|
|
67
|
-
|
|
68
|
-
# Get flagged tasks with specific fields
|
|
69
|
-
of task list --flagged | jq '.[] | {name, project, due}'
|
|
70
|
-
|
|
71
|
-
# Find tasks added more than 2 hours ago
|
|
72
|
-
of inbox list | jq --arg cutoff "$(date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)" \
|
|
73
|
-
'.[] | select(.added < $cutoff)'
|
|
74
|
-
|
|
75
|
-
# Count unprocessed inbox items
|
|
76
|
-
of inbox list | jq '[.[] | select(.project == null)] | length'
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Usage
|
|
80
|
-
|
|
81
|
-
### Task Commands
|
|
82
|
-
|
|
83
|
-
#### List Tasks
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# List all active tasks
|
|
87
|
-
of task list
|
|
88
|
-
|
|
89
|
-
# List flagged tasks only
|
|
90
|
-
of task list --flagged
|
|
91
|
-
|
|
92
|
-
# Filter by project
|
|
93
|
-
of task list --project "Work"
|
|
94
|
-
|
|
95
|
-
# Filter by tag
|
|
96
|
-
of task list --tag "urgent"
|
|
97
|
-
|
|
98
|
-
# Include completed tasks
|
|
99
|
-
of task list --completed
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
#### Create a Task
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# Simple task to inbox
|
|
106
|
-
of task create "Review pull requests"
|
|
107
|
-
|
|
108
|
-
# Task with project
|
|
109
|
-
of task create "Write documentation" --project "Website"
|
|
110
|
-
|
|
111
|
-
# Task with multiple options
|
|
112
|
-
of task create "Call dentist" \
|
|
113
|
-
--project "Personal" \
|
|
114
|
-
--tag "phone" \
|
|
115
|
-
--due "2024-01-15" \
|
|
116
|
-
--flagged \
|
|
117
|
-
--estimate 15
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
#### Update a Task
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# Mark as completed
|
|
124
|
-
of task update "Review pull requests" --complete
|
|
125
|
-
|
|
126
|
-
# Flag a task
|
|
127
|
-
of task update "Call dentist" --flag
|
|
128
|
-
|
|
129
|
-
# Move to different project
|
|
130
|
-
of task update "Write docs" --project "Documentation"
|
|
131
|
-
|
|
132
|
-
# Update multiple properties
|
|
133
|
-
of task update "Email team" \
|
|
134
|
-
--name "Email team about launch" \
|
|
135
|
-
--due "2024-01-20" \
|
|
136
|
-
--flag
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
#### View Task Details
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
# View by name
|
|
143
|
-
of task view "Review pull requests"
|
|
144
|
-
|
|
145
|
-
# View by ID
|
|
146
|
-
of task view "kXu3B-LZfFH"
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### Delete a Task
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
of task delete "Old task"
|
|
153
|
-
of task rm "Old task" # alias
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Project Commands
|
|
157
|
-
|
|
158
|
-
#### List Projects
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
# List all active projects
|
|
162
|
-
of project list
|
|
163
|
-
|
|
164
|
-
# Filter by folder
|
|
165
|
-
of project list --folder "Work"
|
|
166
|
-
|
|
167
|
-
# Filter by status
|
|
168
|
-
of project list --status "on hold"
|
|
169
|
-
|
|
170
|
-
# Include dropped projects
|
|
171
|
-
of project list --dropped
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
#### Create a Project
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
# Simple project
|
|
178
|
-
of project create "Website Redesign"
|
|
179
|
-
|
|
180
|
-
# Project with folder and tags
|
|
181
|
-
of project create "Q1 Planning" \
|
|
182
|
-
--folder "Work" \
|
|
183
|
-
--tag "quarterly" \
|
|
184
|
-
--sequential
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### View Project Details
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
of project view "Website Redesign"
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
#### Delete a Project
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
of project delete "Old Project"
|
|
197
|
-
of project rm "Old Project" # alias
|
|
198
12
|
```
|
|
199
13
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
# List inbox items
|
|
204
|
-
of inbox list
|
|
205
|
-
|
|
206
|
-
# Get inbox count
|
|
207
|
-
of inbox count
|
|
208
|
-
```
|
|
14
|
+
Requires [Bun](https://bun.sh) and macOS with OmniFocus installed.
|
|
209
15
|
|
|
210
|
-
|
|
16
|
+
## Quick Start
|
|
211
17
|
|
|
212
18
|
```bash
|
|
213
|
-
#
|
|
214
|
-
of
|
|
19
|
+
of inbox count # Check inbox
|
|
20
|
+
of task list --flagged # Today's tasks
|
|
21
|
+
of task create "Buy groceries" # Quick capture
|
|
22
|
+
of task update "Buy groceries" --complete # Mark done
|
|
215
23
|
```
|
|
216
24
|
|
|
217
|
-
|
|
25
|
+
## Commands
|
|
218
26
|
|
|
219
|
-
|
|
27
|
+
### Tasks
|
|
220
28
|
|
|
221
29
|
```bash
|
|
222
|
-
# List
|
|
223
|
-
of
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
of
|
|
30
|
+
of task list # List active tasks
|
|
31
|
+
of task list --flagged # Flagged tasks only
|
|
32
|
+
of task list --project "Work" # Filter by project
|
|
33
|
+
of task list --tag "urgent" # Filter by tag
|
|
34
|
+
of task list --completed # Include completed
|
|
227
35
|
|
|
228
|
-
|
|
229
|
-
|
|
36
|
+
of task create "Name" [options]
|
|
37
|
+
--project <name> # Assign to project
|
|
38
|
+
--tag <tags...> # Add tags
|
|
39
|
+
--due <YYYY-MM-DD> # Set due date
|
|
40
|
+
--defer <YYYY-MM-DD> # Set defer date
|
|
41
|
+
--flagged # Flag the task
|
|
42
|
+
--estimate <minutes> # Time estimate
|
|
43
|
+
--note <text> # Add note
|
|
230
44
|
|
|
231
|
-
|
|
232
|
-
|
|
45
|
+
of task update <name|id> [options]
|
|
46
|
+
--complete # Mark completed
|
|
47
|
+
--flag / --unflag # Toggle flag
|
|
48
|
+
--name <new-name> # Rename
|
|
49
|
+
--project/--tag/--due/--defer # Same as create
|
|
233
50
|
|
|
234
|
-
|
|
235
|
-
of
|
|
51
|
+
of task view <name|id> # View details
|
|
52
|
+
of task delete <name|id> # Delete task
|
|
236
53
|
```
|
|
237
54
|
|
|
238
|
-
|
|
55
|
+
### Projects
|
|
239
56
|
|
|
240
57
|
```bash
|
|
241
|
-
|
|
242
|
-
of
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
Displays total tag counts, average tasks per tag, most/least used tags, and stale tags.
|
|
58
|
+
of project list # List active projects
|
|
59
|
+
of project list --folder "Work" # Filter by folder
|
|
60
|
+
of project list --status "on hold" # Filter by status
|
|
61
|
+
of project list --dropped # Include dropped
|
|
246
62
|
|
|
247
|
-
|
|
63
|
+
of project create "Name" [options]
|
|
64
|
+
--folder <name> # Assign to folder
|
|
65
|
+
--tag <tags...> # Add tags
|
|
66
|
+
--sequential # Sequential project
|
|
67
|
+
--note <text> # Add note
|
|
248
68
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
of tag create "New Tag"
|
|
252
|
-
|
|
253
|
-
# Create a nested tag (child of existing tag)
|
|
254
|
-
of tag create "Child Tag" --parent "Parent Tag"
|
|
69
|
+
of project view <name|id> # View details
|
|
70
|
+
of project delete <name|id> # Delete project
|
|
255
71
|
```
|
|
256
72
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
# View tag details by name
|
|
261
|
-
of tag view "Tag Name"
|
|
262
|
-
|
|
263
|
-
# View tag by ID (always unique)
|
|
264
|
-
of tag view "kXu3B-LZfFH"
|
|
265
|
-
|
|
266
|
-
# View nested tag using path syntax
|
|
267
|
-
of tag view "Parent/Child"
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
If multiple tags share the same name in different hierarchies, use the full hierarchical path or tag ID. The command will show available paths if the name is ambiguous.
|
|
271
|
-
|
|
272
|
-
#### Update Tag
|
|
73
|
+
### Tags
|
|
273
74
|
|
|
274
75
|
```bash
|
|
275
|
-
#
|
|
276
|
-
of tag
|
|
76
|
+
of tag list # All tags with counts
|
|
77
|
+
of tag list --unused-days 30 # Stale tags
|
|
78
|
+
of tag list --sort usage # Most used first
|
|
79
|
+
of tag list --sort activity # Most recent first
|
|
80
|
+
of tag list --active-only # Only count incomplete tasks
|
|
277
81
|
|
|
278
|
-
#
|
|
279
|
-
of tag update "Tag Name" --inactive
|
|
82
|
+
of tag stats # Usage statistics
|
|
280
83
|
|
|
281
|
-
#
|
|
282
|
-
of tag
|
|
84
|
+
of tag create "Name" # Create tag
|
|
85
|
+
of tag create "Child" --parent "Parent" # Nested tag
|
|
283
86
|
|
|
284
|
-
|
|
285
|
-
of tag update
|
|
87
|
+
of tag view <name|path|id> # View details
|
|
88
|
+
of tag update <name> --name "New" # Rename
|
|
89
|
+
of tag update <name> --inactive # Deactivate
|
|
90
|
+
of tag delete <name> # Delete tag
|
|
286
91
|
```
|
|
287
92
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
# Delete a tag by name
|
|
292
|
-
of tag delete "Tag Name"
|
|
293
|
-
|
|
294
|
-
# Delete using alias
|
|
295
|
-
of tag rm "Tag Name"
|
|
296
|
-
|
|
297
|
-
# Delete nested tag using path
|
|
298
|
-
of tag delete "Parent/Child"
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Command Reference
|
|
302
|
-
|
|
303
|
-
### Global Options
|
|
304
|
-
|
|
305
|
-
- `-h, --help` - Show help for any command
|
|
306
|
-
- `-V, --version` - Show version number
|
|
307
|
-
- `--compact` - Compact JSON output (single line)
|
|
308
|
-
|
|
309
|
-
### Task Filters
|
|
310
|
-
|
|
311
|
-
- `-f, --flagged` - Show only flagged tasks
|
|
312
|
-
- `-p, --project <name>` - Filter by project name
|
|
313
|
-
- `-t, --tag <name>` - Filter by tag name
|
|
314
|
-
- `-c, --completed` - Include completed tasks
|
|
315
|
-
|
|
316
|
-
### Task Options
|
|
317
|
-
|
|
318
|
-
- `--project <name>` - Assign to project
|
|
319
|
-
- `--note <text>` - Add note
|
|
320
|
-
- `--tag <tags...>` - Add tags (space-separated)
|
|
321
|
-
- `--due <date>` - Set due date (ISO format: YYYY-MM-DD)
|
|
322
|
-
- `--defer <date>` - Set defer date (ISO format)
|
|
323
|
-
- `--flagged` - Flag the task
|
|
324
|
-
- `--estimate <minutes>` - Set time estimate in minutes
|
|
325
|
-
|
|
326
|
-
### Project Options
|
|
327
|
-
|
|
328
|
-
- `--folder <name>` - Assign to folder
|
|
329
|
-
- `--note <text>` - Add note
|
|
330
|
-
- `--tag <tags...>` - Add tags (space-separated)
|
|
331
|
-
- `--sequential` - Make it a sequential project
|
|
332
|
-
- `--status <status>` - Set status (active, on hold, dropped)
|
|
333
|
-
|
|
334
|
-
### Tag Options
|
|
335
|
-
|
|
336
|
-
**List options:**
|
|
337
|
-
- `-u, --unused-days <days>` - Show tags unused for N days
|
|
338
|
-
- `-s, --sort <field>` - Sort by: name, usage, activity (default: name)
|
|
339
|
-
- `-a, --active-only` - Only count active (incomplete) tasks
|
|
340
|
-
|
|
341
|
-
**Create/Update options:**
|
|
342
|
-
- `-p, --parent <name>` - Create as child of parent tag (create only)
|
|
343
|
-
- `-n, --name <name>` - Rename tag (update only)
|
|
344
|
-
- `-a, --active` - Set tag as active (update only)
|
|
345
|
-
- `-i, --inactive` - Set tag as inactive (update only)
|
|
346
|
-
|
|
347
|
-
## Task Object Schema
|
|
348
|
-
|
|
349
|
-
Task objects include these fields:
|
|
350
|
-
|
|
351
|
-
- `id` - Unique identifier
|
|
352
|
-
- `name` - Task name
|
|
353
|
-
- `note` - Notes (or null)
|
|
354
|
-
- `completed` - Boolean completion status
|
|
355
|
-
- `flagged` - Boolean flagged status
|
|
356
|
-
- `project` - Project name (null for inbox items)
|
|
357
|
-
- `tags` - Array of tag names
|
|
358
|
-
- `defer` - Defer date in ISO format (or null)
|
|
359
|
-
- `due` - Due date in ISO format (or null)
|
|
360
|
-
- `estimatedMinutes` - Time estimate in minutes (or null)
|
|
361
|
-
- `completionDate` - Completion timestamp (or null)
|
|
362
|
-
- `added` - Creation timestamp (or null)
|
|
363
|
-
- `modified` - Last modification timestamp (or null)
|
|
364
|
-
|
|
365
|
-
## Examples
|
|
366
|
-
|
|
367
|
-
### Daily Workflow
|
|
93
|
+
### Other
|
|
368
94
|
|
|
369
95
|
```bash
|
|
370
|
-
#
|
|
371
|
-
of inbox count
|
|
372
|
-
|
|
373
|
-
# List today's tasks
|
|
374
|
-
of task list --flagged
|
|
375
|
-
|
|
376
|
-
# Add a quick task
|
|
377
|
-
of task create "Buy groceries" --tag "errands"
|
|
378
|
-
|
|
379
|
-
# Review project status
|
|
380
|
-
of project list
|
|
96
|
+
of inbox list # List inbox items
|
|
97
|
+
of inbox count # Inbox count
|
|
98
|
+
of search "query" # Search tasks
|
|
381
99
|
```
|
|
382
100
|
|
|
383
|
-
|
|
101
|
+
## JSON Output
|
|
384
102
|
|
|
385
|
-
|
|
386
|
-
# Check all projects
|
|
387
|
-
of project list
|
|
388
|
-
|
|
389
|
-
# Review flagged items
|
|
390
|
-
of task list --flagged
|
|
391
|
-
|
|
392
|
-
# Search for specific topics
|
|
393
|
-
of search "meeting"
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### Task Management
|
|
103
|
+
All commands output JSON. Use `--compact` for single-line output.
|
|
397
104
|
|
|
398
105
|
```bash
|
|
399
|
-
|
|
400
|
-
of task
|
|
401
|
-
|
|
402
|
-
--tag "writing" \
|
|
403
|
-
--due "2024-02-01" \
|
|
404
|
-
--estimate 120 \
|
|
405
|
-
--note "Include metrics from Q4 dashboard"
|
|
406
|
-
|
|
407
|
-
# Complete a task
|
|
408
|
-
of task update "Draft quarterly report" --complete
|
|
409
|
-
|
|
410
|
-
# Reschedule a task
|
|
411
|
-
of task update "Team meeting prep" --due "2024-01-22"
|
|
106
|
+
of task list | jq 'length' # Count tasks
|
|
107
|
+
of task list | jq '.[] | .name' # Task names
|
|
108
|
+
of task list --flagged | jq '.[] | {name, due}' # Specific fields
|
|
412
109
|
```
|
|
413
110
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
```bash
|
|
417
|
-
# View all tags with usage stats
|
|
418
|
-
of tag list
|
|
419
|
-
|
|
420
|
-
# Find stale tags not used in 60+ days
|
|
421
|
-
of tag list --unused-days 60
|
|
422
|
-
|
|
423
|
-
# See most used tags
|
|
424
|
-
of tag list --sort usage
|
|
425
|
-
|
|
426
|
-
# View recently active tags
|
|
427
|
-
of tag list --sort activity
|
|
428
|
-
|
|
429
|
-
# Get comprehensive tag statistics
|
|
430
|
-
of tag stats
|
|
431
|
-
|
|
432
|
-
# Create a new tag
|
|
433
|
-
of tag create "Urgent"
|
|
111
|
+
## Task Schema
|
|
434
112
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"id": "kXu3B-LZfFH",
|
|
116
|
+
"name": "Task name",
|
|
117
|
+
"completed": false,
|
|
118
|
+
"flagged": true,
|
|
119
|
+
"project": "Project Name",
|
|
120
|
+
"tags": ["tag1", "tag2"],
|
|
121
|
+
"due": "2024-01-15T00:00:00.000Z",
|
|
122
|
+
"defer": null,
|
|
123
|
+
"estimatedMinutes": 30,
|
|
124
|
+
"note": "Notes here",
|
|
125
|
+
"added": "2024-01-01T10:00:00.000Z",
|
|
126
|
+
"modified": "2024-01-10T15:30:00.000Z",
|
|
127
|
+
"completionDate": null
|
|
128
|
+
}
|
|
450
129
|
```
|
|
451
130
|
|
|
452
|
-
##
|
|
131
|
+
## Troubleshooting
|
|
453
132
|
|
|
454
|
-
|
|
455
|
-
- OmniFocus installed and running
|
|
456
|
-
- [Bun](https://bun.sh) 1.0+
|
|
133
|
+
**Permission denied**: Grant automation permission in System Settings > Privacy & Security > Automation.
|
|
457
134
|
|
|
458
|
-
|
|
135
|
+
**Task not found**: Use exact name or ID. IDs appear in JSON output.
|
|
459
136
|
|
|
460
|
-
|
|
137
|
+
**Date format**: Use ISO format `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`.
|
|
461
138
|
|
|
462
139
|
## Development
|
|
463
140
|
|
|
464
141
|
```bash
|
|
465
|
-
|
|
142
|
+
git clone https://github.com/stephendolan/omnifocus-cli.git
|
|
143
|
+
cd omnifocus-cli
|
|
466
144
|
bun install
|
|
467
|
-
|
|
468
|
-
#
|
|
469
|
-
bun run build
|
|
470
|
-
|
|
471
|
-
# Watch mode for development
|
|
472
|
-
bun run dev
|
|
473
|
-
|
|
474
|
-
# Link for local testing
|
|
475
|
-
bun link
|
|
145
|
+
bun run dev # Watch mode
|
|
146
|
+
bun link # Link globally as `of`
|
|
476
147
|
```
|
|
477
148
|
|
|
478
|
-
## Troubleshooting
|
|
479
|
-
|
|
480
|
-
### Permission Issues
|
|
481
|
-
|
|
482
|
-
When you first run a command, macOS will ask for permission to control OmniFocus. Make sure to grant this permission in System Settings > Privacy & Security > Automation.
|
|
483
|
-
|
|
484
|
-
### Task Not Found
|
|
485
|
-
|
|
486
|
-
Use the exact task name or ID. Task IDs are included in the JSON output of all list commands.
|
|
487
|
-
|
|
488
|
-
### Date Format
|
|
489
|
-
|
|
490
|
-
Use ISO format for dates: `YYYY-MM-DD` or full ISO strings like `2024-01-15T10:00:00`.
|
|
491
|
-
|
|
492
149
|
## License
|
|
493
150
|
|
|
494
151
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -369,7 +369,7 @@ var OmniFocus = class {
|
|
|
369
369
|
updates.push(`task.flagged = ${options.flagged};`);
|
|
370
370
|
}
|
|
371
371
|
if (options.completed !== void 0) {
|
|
372
|
-
updates.push(
|
|
372
|
+
updates.push(options.completed ? "task.markComplete();" : "task.markIncomplete();");
|
|
373
373
|
}
|
|
374
374
|
if (options.estimatedMinutes !== void 0) {
|
|
375
375
|
updates.push(`task.estimatedMinutes = ${options.estimatedMinutes};`);
|
|
@@ -1162,6 +1162,21 @@ function createInboxCommand() {
|
|
|
1162
1162
|
outputJson({ count });
|
|
1163
1163
|
})
|
|
1164
1164
|
);
|
|
1165
|
+
command.command("add <name>").description("Add a task to inbox").option("--note <text>", "Add note").option("-t, --tag <tags...>", "Add tags").option("-d, --due <date>", "Set due date").option("-D, --defer <date>", "Set defer date").option("-f, --flagged", "Flag the task").option("-e, --estimate <minutes>", "Estimated time in minutes", parseInt).action(
|
|
1166
|
+
withErrorHandling(async (name, options) => {
|
|
1167
|
+
const of = new OmniFocus();
|
|
1168
|
+
const task = await of.createTask({
|
|
1169
|
+
name,
|
|
1170
|
+
note: options.note,
|
|
1171
|
+
tags: options.tag,
|
|
1172
|
+
due: options.due ? parseDateTime(options.due) : void 0,
|
|
1173
|
+
defer: options.defer ? parseDateTime(options.defer) : void 0,
|
|
1174
|
+
flagged: options.flagged,
|
|
1175
|
+
estimatedMinutes: options.estimate
|
|
1176
|
+
});
|
|
1177
|
+
outputJson(task);
|
|
1178
|
+
})
|
|
1179
|
+
);
|
|
1165
1180
|
return command;
|
|
1166
1181
|
}
|
|
1167
1182
|
|
|
@@ -1290,7 +1305,7 @@ function createFolderCommand() {
|
|
|
1290
1305
|
|
|
1291
1306
|
// src/cli.ts
|
|
1292
1307
|
var program = new Command8();
|
|
1293
|
-
program.name("of").description("A command-line interface for OmniFocus on macOS").version("2.1
|
|
1308
|
+
program.name("of").description("A command-line interface for OmniFocus on macOS").version("2.2.1").option("-c, --compact", "Minified JSON output (single line)").hook("preAction", (thisCommand) => {
|
|
1294
1309
|
const options = thisCommand.opts();
|
|
1295
1310
|
setOutputOptions({
|
|
1296
1311
|
compact: options.compact
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/lib/output.ts","../src/commands/task.ts","../src/lib/errors.ts","../src/lib/command-utils.ts","../src/lib/omnifocus.ts","../src/lib/dates.ts","../src/commands/project.ts","../src/commands/inbox.ts","../src/commands/search.ts","../src/commands/perspective.ts","../src/commands/tag.ts","../src/commands/folder.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { Command } from 'commander';\nimport { setOutputOptions } from './lib/output.js';\nimport { createTaskCommand } from './commands/task.js';\nimport { createProjectCommand } from './commands/project.js';\nimport { createInboxCommand } from './commands/inbox.js';\nimport { createSearchCommand } from './commands/search.js';\nimport { createPerspectiveCommand } from './commands/perspective.js';\nimport { createTagCommand } from './commands/tag.js';\nimport { createFolderCommand } from './commands/folder.js';\n\nconst program = new Command();\n\nprogram\n .name('of')\n .description('A command-line interface for OmniFocus on macOS')\n .version(__VERSION__)\n .option('-c, --compact', 'Minified JSON output (single line)')\n .hook('preAction', (thisCommand) => {\n const options = thisCommand.opts();\n setOutputOptions({\n compact: options.compact,\n });\n });\n\nprogram.addCommand(createTaskCommand());\nprogram.addCommand(createProjectCommand());\nprogram.addCommand(createInboxCommand());\nprogram.addCommand(createSearchCommand());\nprogram.addCommand(createPerspectiveCommand());\nprogram.addCommand(createTagCommand());\nprogram.addCommand(createFolderCommand());\n\nprogram.parseAsync().catch(() => {\n process.exit(1);\n});\n","export interface OutputOptions {\n compact?: boolean;\n}\n\nlet globalOutputOptions: OutputOptions = {};\n\nexport function setOutputOptions(options: OutputOptions): void {\n globalOutputOptions = options;\n}\n\nexport function outputJson(data: unknown, options: OutputOptions = {}): void {\n const mergedOptions = { ...globalOutputOptions, ...options };\n const jsonString = mergedOptions.compact ? JSON.stringify(data) : JSON.stringify(data, null, 2);\n\n console.log(jsonString);\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport { parseDateTime } from '../lib/dates.js';\nimport type { TaskFilters, UpdateTaskOptions } from '../types.js';\n\nexport function createTaskCommand(): Command {\n const command = new Command('task');\n command.description('Manage OmniFocus tasks');\n\n command\n .command('list')\n .alias('ls')\n .description('List tasks')\n .option('-f, --flagged', 'Show only flagged tasks')\n .option('-p, --project <name>', 'Filter by project')\n .option('-t, --tag <name>', 'Filter by tag')\n .option('-c, --completed', 'Include completed tasks')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const filters: TaskFilters = {\n includeCompleted: options.completed,\n ...(options.flagged && { flagged: true }),\n ...(options.project && { project: options.project }),\n ...(options.tag && { tag: options.tag }),\n };\n const tasks = await of.listTasks(filters);\n outputJson(tasks);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new task')\n .option('-p, --project <name>', 'Assign to project')\n .option('--note <text>', 'Add note')\n .option('-t, --tag <tags...>', 'Add tags')\n .option('-d, --due <date>', 'Set due date')\n .option('-D, --defer <date>', 'Set defer date')\n .option('-f, --flagged', 'Flag the task')\n .option('-e, --estimate <minutes>', 'Estimated time in minutes', parseInt)\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const task = await of.createTask({\n name,\n note: options.note,\n project: options.project,\n tags: options.tag,\n due: options.due ? parseDateTime(options.due) : undefined,\n defer: options.defer ? parseDateTime(options.defer) : undefined,\n flagged: options.flagged,\n estimatedMinutes: options.estimate,\n });\n outputJson(task);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing task')\n .option('-n, --name <name>', 'New name')\n .option('--note <text>', 'New note')\n .option('-p, --project <name>', 'Move to project')\n .option('-t, --tag <tags...>', 'Replace tags')\n .option('-d, --due <date>', 'Set due date')\n .option('-D, --defer <date>', 'Set defer date')\n .option('-f, --flag', 'Flag the task')\n .option('-F, --unflag', 'Unflag the task')\n .option('-c, --complete', 'Mark as completed')\n .option('-C, --incomplete', 'Mark as incomplete')\n .option('-e, --estimate <minutes>', 'Estimated time in minutes', parseInt)\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateTaskOptions = {\n ...(options.name && { name: options.name }),\n ...(options.note !== undefined && { note: options.note }),\n ...(options.project && { project: options.project }),\n ...(options.tag && { tags: options.tag }),\n ...(options.due !== undefined && {\n due: options.due ? parseDateTime(options.due) : null,\n }),\n ...(options.defer !== undefined && {\n defer: options.defer ? parseDateTime(options.defer) : null,\n }),\n ...(options.flag && { flagged: true }),\n ...(options.unflag && { flagged: false }),\n ...(options.complete && { completed: true }),\n ...(options.incomplete && { completed: false }),\n ...(options.estimate !== undefined && { estimatedMinutes: options.estimate }),\n };\n const task = await of.updateTask(idOrName, updates);\n outputJson(task);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a task')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteTask(idOrName);\n outputJson({ message: 'Task deleted successfully' });\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View task details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const task = await of.getTask(idOrName);\n outputJson(task);\n })\n );\n\n command\n .command('stats')\n .description('Show task statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getTaskStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { outputJson } from './output.js';\n\nexport class OmniFocusCliError extends Error {\n constructor(\n message: string,\n public statusCode: number = 500\n ) {\n super(message);\n this.name = 'OmniFocusCliError';\n }\n}\n\nexport function handleError(error: unknown): never {\n let name = 'unknown_error';\n let detail = 'An unknown error occurred';\n let statusCode = 500;\n\n if (error instanceof OmniFocusCliError) {\n name = 'cli_error';\n detail = error.message;\n statusCode = error.statusCode;\n } else if (error instanceof Error) {\n name = 'omnifocus_error';\n detail = error.message;\n\n if (detail.includes('not found')) {\n statusCode = 404;\n } else if (detail.includes('Multiple')) {\n statusCode = 400;\n }\n }\n\n outputJson({ error: { name, detail, statusCode } });\n process.exit(1);\n}\n","import { handleError } from './errors.js';\n\nexport function withErrorHandling<T extends unknown[], R>(\n fn: (...args: T) => Promise<R>\n): (...args: T) => Promise<void> {\n return async (...args: T) => {\n try {\n await fn(...args);\n } catch (error) {\n handleError(error);\n }\n };\n}\n","import { execFile } from 'child_process';\nimport { writeFile, unlink } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\nimport { promisify } from 'util';\nimport type {\n Task,\n Project,\n TaskFilters,\n ProjectFilters,\n CreateTaskOptions,\n UpdateTaskOptions,\n CreateProjectOptions,\n UpdateProjectOptions,\n Perspective,\n Tag,\n TagListOptions,\n TagStats,\n TaskStats,\n ProjectStats,\n CreateTagOptions,\n UpdateTagOptions,\n Folder,\n FolderFilters,\n} from '../types.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport class OmniFocus {\n private readonly PROJECT_STATUS_MAP = {\n active: 'Active',\n 'on hold': 'OnHold',\n dropped: 'Dropped',\n } as const;\n\n private readonly OMNI_HELPERS = `\n function serializeTask(task) {\n const containingProject = task.containingProject;\n const tagNames = task.tags.map(t => t.name);\n\n return {\n id: task.id.primaryKey,\n name: task.name,\n note: task.note || null,\n completed: task.completed,\n dropped: task.dropped,\n effectivelyActive: task.effectiveActive,\n flagged: task.flagged,\n project: containingProject ? containingProject.name : null,\n tags: tagNames,\n defer: task.deferDate ? task.deferDate.toISOString() : null,\n due: task.dueDate ? task.dueDate.toISOString() : null,\n estimatedMinutes: task.estimatedMinutes || null,\n completionDate: task.completionDate ? task.completionDate.toISOString() : null,\n added: task.added ? task.added.toISOString() : null,\n modified: task.modified ? task.modified.toISOString() : null\n };\n }\n\n function serializeProject(project) {\n const parentFolder = project.parentFolder;\n const allTasks = project.flattenedTasks;\n const remainingTasks = allTasks.filter(t => !t.completed);\n const tagNames = project.tags.map(t => t.name);\n\n return {\n id: project.id.primaryKey,\n name: project.name,\n note: project.note || null,\n status: projectStatusToString(project.status),\n folder: parentFolder ? parentFolder.name : null,\n sequential: project.sequential,\n taskCount: allTasks.length,\n remainingCount: remainingTasks.length,\n tags: tagNames\n };\n }\n\n function findTask(idOrName) {\n for (const task of flattenedTasks) {\n if (task.id.primaryKey === idOrName || task.name === idOrName) {\n return task;\n }\n }\n throw new Error(\"Task not found: \" + idOrName);\n }\n\n function findProject(idOrName) {\n for (const project of flattenedProjects) {\n if (project.id.primaryKey === idOrName || project.name === idOrName) {\n return project;\n }\n }\n throw new Error(\"Project not found: \" + idOrName);\n }\n\n function getTagPath(tag) {\n const parts = [tag.name];\n let current = tag.parent;\n while (current) {\n parts.unshift(current.name);\n current = current.parent;\n }\n return parts.join('/');\n }\n\n function findTag(idOrName) {\n for (const tag of flattenedTags) {\n if (tag.id.primaryKey === idOrName) {\n return tag;\n }\n }\n\n if (idOrName.includes('/')) {\n for (const tag of flattenedTags) {\n if (getTagPath(tag) === idOrName) {\n return tag;\n }\n }\n throw new Error(\"Tag not found: \" + idOrName);\n }\n\n const matches = flattenedTags.filter(tag => tag.name === idOrName);\n\n if (matches.length === 0) {\n throw new Error(\"Tag not found: \" + idOrName);\n }\n\n if (matches.length > 1) {\n const paths = matches.map(getTagPath);\n throw new Error(\"Multiple tags found with name '\" + idOrName + \"'. Please use full path:\\\\n \" + paths.join('\\\\n ') + \"\\\\nOr use tag ID: \" + matches.map(t => t.id.primaryKey).join(', '));\n }\n\n return matches[0];\n }\n\n function findByName(collection, name, typeName) {\n for (const item of collection) {\n if (item.name === name) {\n return item;\n }\n }\n throw new Error(typeName + \" not found: \" + name);\n }\n\n function assignTags(target, tagNames) {\n for (const tagName of tagNames) {\n const tag = findTag(tagName);\n target.addTag(tag);\n }\n }\n\n function replaceTagsOn(target, tagNames) {\n target.clearTags();\n assignTags(target, tagNames);\n }\n\n function statusToString(status, StatusEnum) {\n if (status === StatusEnum.Active) return 'active';\n if (status === StatusEnum.OnHold) return 'on hold';\n if (status === StatusEnum.Dropped) return 'dropped';\n if (status === StatusEnum.Done) return 'done';\n return 'dropped';\n }\n\n function stringToStatus(str, StatusEnum) {\n if (str === 'active') return StatusEnum.Active;\n if (str === 'on hold') return StatusEnum.OnHold;\n return StatusEnum.Dropped;\n }\n\n const projectStatusToString = (status) => statusToString(status, Project.Status);\n const tagStatusToString = (status) => statusToString(status, Tag.Status);\n const folderStatusToString = (status) => {\n if (status === Folder.Status.Active) return 'active';\n return 'dropped';\n };\n const stringToProjectStatus = (str) => stringToStatus(str, Project.Status);\n const stringToTagStatus = (str) => stringToStatus(str, Tag.Status);\n\n function serializeFolder(folder, includeDropped = false) {\n let childFolders = folder.folders;\n if (!includeDropped) {\n childFolders = childFolders.filter(c => c.effectiveActive);\n }\n\n return {\n id: folder.id.primaryKey,\n name: folder.name,\n status: folderStatusToString(folder.status),\n effectivelyActive: folder.effectiveActive,\n parent: folder.parent ? folder.parent.name : null,\n projectCount: folder.projects.length,\n remainingProjectCount: folder.projects.filter(p => p.effectiveActive).length,\n folderCount: folder.folders.length,\n children: childFolders.map(child => serializeFolder(child, includeDropped))\n };\n }\n\n function computeTopItems(items, keyFn, topN = 5) {\n return items\n .sort((a, b) => b[keyFn] - a[keyFn])\n .slice(0, topN)\n .map(item => ({ name: item.name, [keyFn]: item[keyFn] }));\n }\n\n function computeAverage(total, count) {\n return count > 0 ? Math.round((total / count) * 10) / 10 : 0;\n }\n\n function serializeTag(tag, activeOnly = false) {\n const tasks = tag.tasks;\n const remainingTasks = tag.remainingTasks;\n const includedTasks = activeOnly ? remainingTasks : tasks;\n\n const dates = [];\n if (tag.added) dates.push(tag.added);\n if (tag.modified) dates.push(tag.modified);\n\n for (const task of includedTasks) {\n if (task.added) dates.push(task.added);\n if (task.modified) dates.push(task.modified);\n if (!activeOnly && task.completionDate) dates.push(task.completionDate);\n if (!activeOnly && task.effectiveCompletionDate) dates.push(task.effectiveCompletionDate);\n }\n\n const lastActivity = dates.length > 0\n ? dates.reduce((latest, current) => current > latest ? current : latest)\n : null;\n\n return {\n id: tag.id.primaryKey,\n name: tag.name,\n taskCount: includedTasks.length,\n remainingTaskCount: remainingTasks.length,\n added: tag.added ? tag.added.toISOString() : null,\n modified: tag.modified ? tag.modified.toISOString() : null,\n lastActivity: lastActivity ? lastActivity.toISOString() : null,\n active: tag.active,\n status: tagStatusToString(tag.status),\n parent: tag.parent ? tag.parent.name : null,\n children: tag.children.map(c => c.name),\n allowsNextAction: tag.allowsNextAction\n };\n }\n `;\n\n private async executeJXA(script: string, timeoutMs = 30000): Promise<string> {\n const tmpFile = join(tmpdir(), `omnifocus-${Date.now()}.js`);\n\n try {\n await writeFile(tmpFile, script, 'utf-8');\n\n const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', tmpFile], {\n timeout: timeoutMs,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return stdout.trim();\n } finally {\n try {\n await unlink(tmpFile);\n } catch {\n /* ignore cleanup errors */\n }\n }\n }\n\n private escapeString(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n }\n\n private wrapOmniScript(omniScript: string): string {\n return `\n const app = Application('OmniFocus');\n app.includeStandardAdditions = true;\n const result = app.evaluateJavascript(${JSON.stringify(omniScript.trim())});\n result;\n `.trim();\n }\n\n private buildTaskFilters(filters: TaskFilters): string {\n const conditions: string[] = [];\n\n if (!filters.includeCompleted) {\n conditions.push('if (task.completed) continue;');\n }\n if (!filters.includeDropped) {\n conditions.push('if (!task.effectiveActive) continue;');\n }\n if (filters.flagged) {\n conditions.push('if (!task.flagged) continue;');\n conditions.push('if (task.taskStatus !== Task.Status.Available) continue;');\n }\n if (filters.project) {\n conditions.push(`\n if (!task.containingProject || task.containingProject.name !== \"${this.escapeString(filters.project)}\") {\n continue;\n }\n `);\n }\n if (filters.tag) {\n conditions.push(`\n if (!task.tags.some(t => t.name === \"${this.escapeString(filters.tag)}\")) {\n continue;\n }\n `);\n }\n\n return conditions.join('\\n ');\n }\n\n private buildProjectFilters(filters: ProjectFilters): string {\n const conditions: string[] = [];\n\n if (!filters.includeDropped) {\n conditions.push(\n 'if (project.status === Project.Status.Dropped || project.status === Project.Status.Done) continue;'\n );\n conditions.push(\n 'if (project.parentFolder && !project.parentFolder.effectiveActive) continue;'\n );\n }\n if (filters.status) {\n const statusCheck = this.PROJECT_STATUS_MAP[filters.status];\n conditions.push(`if (project.status !== Project.Status.${statusCheck}) continue;`);\n }\n if (filters.folder) {\n conditions.push(\n `if (!project.parentFolder || project.parentFolder.name !== \"${this.escapeString(filters.folder)}\") continue;`\n );\n }\n\n return conditions.join('\\n ');\n }\n\n private buildTaskUpdates(options: UpdateTaskOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`task.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.note !== undefined) {\n updates.push(`task.note = \"${this.escapeString(options.note)}\";`);\n }\n if (options.flagged !== undefined) {\n updates.push(`task.flagged = ${options.flagged};`);\n }\n if (options.completed !== undefined) {\n updates.push(`task.completed = ${options.completed};`);\n }\n if (options.estimatedMinutes !== undefined) {\n updates.push(`task.estimatedMinutes = ${options.estimatedMinutes};`);\n }\n if (options.defer !== undefined) {\n updates.push(\n options.defer\n ? `task.deferDate = new Date(${JSON.stringify(options.defer)});`\n : 'task.deferDate = null;'\n );\n }\n if (options.due !== undefined) {\n updates.push(\n options.due\n ? `task.dueDate = new Date(${JSON.stringify(options.due)});`\n : 'task.dueDate = null;'\n );\n }\n if (options.project !== undefined && options.project) {\n updates.push(`\n const targetProject = findByName(flattenedProjects, \"${this.escapeString(options.project)}\", \"Project\");\n moveTasks([task], targetProject);\n `);\n }\n if (options.tags !== undefined) {\n updates.push(`replaceTagsOn(task, ${JSON.stringify(options.tags)});`);\n }\n\n return updates.join('\\n ');\n }\n\n private buildTagUpdates(options: UpdateTagOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`tag.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.status !== undefined) {\n updates.push(`tag.status = stringToTagStatus(\"${options.status}\");`);\n }\n\n return updates.join('\\n ');\n }\n\n private buildProjectUpdates(options: UpdateProjectOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`project.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.note !== undefined) {\n updates.push(`project.note = \"${this.escapeString(options.note)}\";`);\n }\n if (options.sequential !== undefined) {\n updates.push(`project.sequential = ${options.sequential};`);\n }\n if (options.status !== undefined) {\n updates.push(`project.status = stringToProjectStatus(\"${options.status}\");`);\n }\n if (options.folder !== undefined && options.folder) {\n updates.push(`\n const targetFolder = findByName(flattenedFolders, \"${this.escapeString(options.folder)}\", \"Folder\");\n moveProjects([project], targetFolder);\n `);\n }\n if (options.tags !== undefined) {\n updates.push(`replaceTagsOn(project, ${JSON.stringify(options.tags)});`);\n }\n\n return updates.join('\\n ');\n }\n\n async listTasks(filters: TaskFilters = {}): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n for (const task of flattenedTasks) {\n ${this.buildTaskFilters(filters)}\n results.push(serializeTask(task));\n }\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createTask(options: CreateTaskOptions): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.project\n ? `const targetProject = findByName(flattenedProjects, \"${this.escapeString(options.project)}\", \"Project\");\n const task = new Task(\"${this.escapeString(options.name)}\", targetProject);`\n : `const task = new Task(\"${this.escapeString(options.name)}\");`\n }\n\n ${options.note ? `task.note = \"${this.escapeString(options.note)}\";` : ''}\n ${options.flagged ? 'task.flagged = true;' : ''}\n ${options.estimatedMinutes ? `task.estimatedMinutes = ${options.estimatedMinutes};` : ''}\n ${options.defer ? `task.deferDate = new Date(${JSON.stringify(options.defer)});` : ''}\n ${options.due ? `task.dueDate = new Date(${JSON.stringify(options.due)});` : ''}\n ${options.tags && options.tags.length > 0 ? `assignTags(task, ${JSON.stringify(options.tags)});` : ''}\n\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateTask(idOrName: string, options: UpdateTaskOptions): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const task = findTask(\"${this.escapeString(idOrName)}\");\n ${this.buildTaskUpdates(options)}\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteTask(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findTask(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async listProjects(filters: ProjectFilters = {}): Promise<Project[]> {\n const filterCode = this.buildProjectFilters(filters);\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n for (const project of flattenedProjects) {\n ${filterCode}\n results.push(serializeProject(project));\n }\n return JSON.stringify(results);\n })();\n `;\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createProject(options: CreateProjectOptions): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.folder\n ? `const targetFolder = findByName(flattenedFolders, \"${this.escapeString(options.folder)}\", \"Folder\");\n const project = new Project(\"${this.escapeString(options.name)}\", targetFolder);`\n : `const project = new Project(\"${this.escapeString(options.name)}\");`\n }\n\n ${options.note ? `project.note = \"${this.escapeString(options.note)}\";` : ''}\n ${options.sequential !== undefined ? `project.sequential = ${options.sequential};` : ''}\n ${options.status ? `project.status = stringToProjectStatus(\"${options.status}\");` : ''}\n ${options.tags && options.tags.length > 0 ? `assignTags(project, ${JSON.stringify(options.tags)});` : ''}\n\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateProject(idOrName: string, options: UpdateProjectOptions): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const project = findProject(\"${this.escapeString(idOrName)}\");\n ${this.buildProjectUpdates(options)}\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteProject(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findProject(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async listInboxTasks(): Promise<Task[]> {\n return this.getPerspectiveTasks('Inbox');\n }\n\n async getInboxCount(): Promise<number> {\n const tasks = await this.getPerspectiveTasks('Inbox');\n return tasks.length;\n }\n\n async searchTasks(query: string): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n const searchQuery = \"${this.escapeString(query)}\".toLowerCase();\n\n for (const task of flattenedTasks) {\n if (task.completed) continue;\n if (!task.effectiveActive) continue;\n\n const name = task.name.toLowerCase();\n const note = (task.note || '').toLowerCase();\n\n if (name.includes(searchQuery) || note.includes(searchQuery)) {\n results.push(serializeTask(task));\n }\n }\n\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getTask(idOrName: string): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const task = findTask(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getProject(idOrName: string): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const project = findProject(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async listPerspectives(): Promise<Perspective[]> {\n const omniScript = `\n (() => {\n const results = [];\n\n const builtInNames = ['Inbox', 'Flagged', 'Forecast', 'Projects', 'Tags', 'Nearby', 'Review'];\n for (const name of builtInNames) {\n results.push({ id: name, name: name });\n }\n\n const customPerspectives = Perspective.Custom.all;\n for (const perspective of customPerspectives) {\n results.push({ id: perspective.name, name: perspective.name });\n }\n\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getPerspectiveTasks(perspectiveName: string): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const doc = document;\n const windows = doc.windows;\n\n if (windows.length === 0) {\n throw new Error(\"No OmniFocus window is open. Please open an OmniFocus window and try again.\");\n }\n\n const win = windows[0];\n const perspectiveName = \"${this.escapeString(perspectiveName)}\";\n\n const builtInPerspectives = {\n 'inbox': Perspective.BuiltIn.Inbox,\n 'flagged': Perspective.BuiltIn.Flagged,\n 'forecast': Perspective.BuiltIn.Forecast,\n 'projects': Perspective.BuiltIn.Projects,\n 'tags': Perspective.BuiltIn.Tags,\n 'nearby': Perspective.BuiltIn.Nearby,\n 'review': Perspective.BuiltIn.Review\n };\n\n const lowerName = perspectiveName.toLowerCase();\n if (builtInPerspectives[lowerName]) {\n win.perspective = builtInPerspectives[lowerName];\n } else {\n const customPerspective = Perspective.Custom.byName(perspectiveName);\n if (customPerspective) {\n win.perspective = customPerspective;\n } else {\n throw new Error(\"Perspective not found: \" + perspectiveName);\n }\n }\n\n const content = win.content;\n if (!content) {\n throw new Error(\"No content available in window\");\n }\n\n const tasks = [];\n content.rootNode.apply(node => {\n const obj = node.object;\n if (obj instanceof Task) {\n tasks.push(serializeTask(obj));\n }\n });\n\n return JSON.stringify(tasks);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript), 60000);\n return JSON.parse(output);\n }\n\n async listTags(options: TagListOptions = {}): Promise<Tag[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n const now = new Date();\n const activeOnly = ${!!options.activeOnly};\n\n for (const tag of flattenedTags) {\n const serialized = serializeTag(tag, activeOnly);\n results.push(serialized);\n }\n\n ${\n options.unusedDays\n ? `\n const cutoffDate = new Date(now.getTime() - (${options.unusedDays} * 24 * 60 * 60 * 1000));\n const filtered = results.filter(tag => {\n if (!tag.lastActivity) return true;\n return new Date(tag.lastActivity) < cutoffDate;\n });\n return JSON.stringify(filtered);\n `\n : 'return JSON.stringify(results);'\n }\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n const tags = JSON.parse(output);\n\n return this.sortTags(tags, options.sortBy);\n }\n\n private sortTags(tags: Tag[], sortBy: string = 'name'): Tag[] {\n const sortFns: Record<string, (a: Tag, b: Tag) => number> = {\n usage: (a, b) => b.taskCount - a.taskCount,\n activity: (a, b) => {\n if (!a.lastActivity && !b.lastActivity) return 0;\n if (!a.lastActivity) return 1;\n if (!b.lastActivity) return -1;\n return new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime();\n },\n name: (a, b) => a.name.localeCompare(b.name),\n };\n\n return tags.sort(sortFns[sortBy] || sortFns.name);\n }\n\n async getTagStats(): Promise<TagStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allTags = [];\n for (const tag of flattenedTags) {\n allTags.push(serializeTag(tag));\n }\n\n const activeTags = allTags.filter(t => t.active);\n const tagsWithTasks = allTags.filter(t => t.taskCount > 0);\n const unusedTags = allTags.filter(t => t.taskCount === 0);\n\n const totalTasks = tagsWithTasks.reduce((sum, t) => sum + t.taskCount, 0);\n const avgTasksPerTag = computeAverage(totalTasks, tagsWithTasks.length);\n\n const mostUsedTags = computeTopItems(allTags, 'taskCount');\n const leastUsedTags = computeTopItems(\n tagsWithTasks.map(t => ({ ...t, taskCount: -t.taskCount })),\n 'taskCount'\n ).map(t => ({ name: t.name, taskCount: -t.taskCount }));\n\n const now = new Date();\n const thirtyDaysAgo = new Date(now.getTime() - (30 * 24 * 60 * 60 * 1000));\n const staleTags = allTags\n .filter(t => t.lastActivity && new Date(t.lastActivity) < thirtyDaysAgo)\n .map(t => ({\n name: t.name,\n daysSinceActivity: Math.floor((now - new Date(t.lastActivity)) / (24 * 60 * 60 * 1000))\n }))\n .sort((a, b) => b.daysSinceActivity - a.daysSinceActivity);\n\n return JSON.stringify({\n totalTags: allTags.length,\n activeTags: activeTags.length,\n tagsWithTasks: tagsWithTasks.length,\n unusedTags: unusedTags.length,\n avgTasksPerTag,\n mostUsedTags,\n leastUsedTags,\n staleTags\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createTag(options: CreateTagOptions): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.parent\n ? `const parentTag = findTag(\"${this.escapeString(options.parent)}\");\n const tag = new Tag(\"${this.escapeString(options.name)}\", parentTag);`\n : `const tag = new Tag(\"${this.escapeString(options.name)}\", tags.beginning);`\n }\n\n ${options.status ? `tag.status = stringToTagStatus(\"${options.status}\");` : ''}\n\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getTag(idOrName: string): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const tag = findTag(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateTag(idOrName: string, options: UpdateTagOptions): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const tag = findTag(\"${this.escapeString(idOrName)}\");\n ${this.buildTagUpdates(options)}\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteTag(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findTag(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async getTaskStats(): Promise<TaskStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allTasks = Array.from(flattenedTasks);\n const now = new Date();\n\n const activeTasks = allTasks.filter(t => !t.completed && t.effectiveActive);\n const completedTasks = allTasks.filter(t => t.completed);\n const flaggedTasks = activeTasks.filter(t => t.flagged);\n const overdueActiveTasks = activeTasks.filter(t => t.dueDate && t.dueDate < now);\n\n const tasksWithEstimates = allTasks.filter(t => t.estimatedMinutes && t.estimatedMinutes > 0);\n const totalEstimatedMinutes = tasksWithEstimates.reduce((sum, t) => sum + (t.estimatedMinutes || 0), 0);\n const avgEstimatedMinutes = tasksWithEstimates.length > 0\n ? Math.round(totalEstimatedMinutes / tasksWithEstimates.length)\n : null;\n\n const totalNonDropped = allTasks.filter(t => t.effectiveActive || t.completed).length;\n const completionRate = totalNonDropped > 0\n ? Math.round((completedTasks.length / totalNonDropped) * 100)\n : 0;\n\n const projectCounts = {};\n for (const task of allTasks) {\n if (!task.effectiveActive && !task.completed) continue;\n const projectName = task.containingProject ? task.containingProject.name : 'Inbox';\n projectCounts[projectName] = (projectCounts[projectName] || 0) + 1;\n }\n const tasksByProject = computeTopItems(\n Object.entries(projectCounts).map(([name, count]) => ({ name, taskCount: count })),\n 'taskCount'\n );\n\n const tagCounts = {};\n for (const task of allTasks) {\n if (!task.effectiveActive && !task.completed) continue;\n for (const tag of task.tags) {\n tagCounts[tag.name] = (tagCounts[tag.name] || 0) + 1;\n }\n }\n const tasksByTag = computeTopItems(\n Object.entries(tagCounts).map(([name, count]) => ({ name, taskCount: count })),\n 'taskCount'\n );\n\n return JSON.stringify({\n totalTasks: allTasks.length,\n activeTasks: activeTasks.length,\n completedTasks: completedTasks.length,\n flaggedTasks: flaggedTasks.length,\n overdueActiveTasks: overdueActiveTasks.length,\n avgEstimatedMinutes,\n tasksWithEstimates: tasksWithEstimates.length,\n completionRate,\n tasksByProject,\n tasksByTag\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getProjectStats(): Promise<ProjectStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allProjects = Array.from(flattenedProjects);\n\n function isProjectEffectivelyActive(p) {\n if (p.status === Project.Status.Dropped || p.status === Project.Status.Done) return false;\n if (p.parentFolder && !p.parentFolder.effectiveActive) return false;\n return true;\n }\n\n const effectivelyActiveProjects = allProjects.filter(isProjectEffectivelyActive);\n const activeProjects = effectivelyActiveProjects.filter(p => p.status === Project.Status.Active);\n const onHoldProjects = effectivelyActiveProjects.filter(p => p.status === Project.Status.OnHold);\n const droppedProjects = allProjects.filter(p => p.status === Project.Status.Dropped);\n const doneProjects = allProjects.filter(p => p.status === Project.Status.Done);\n const sequentialProjects = effectivelyActiveProjects.filter(p => p.sequential);\n const parallelProjects = effectivelyActiveProjects.filter(p => !p.sequential);\n\n const totalTasks = effectivelyActiveProjects.reduce((sum, p) => sum + p.flattenedTasks.length, 0);\n const totalRemaining = effectivelyActiveProjects.reduce((sum, p) => {\n return sum + p.flattenedTasks.filter(t => !t.completed).length;\n }, 0);\n\n const avgTasksPerProject = computeAverage(totalTasks, effectivelyActiveProjects.length);\n const avgRemainingPerProject = computeAverage(totalRemaining, effectivelyActiveProjects.length);\n\n const completionRates = effectivelyActiveProjects\n .filter(p => p.flattenedTasks.length > 0)\n .map(p => {\n const total = p.flattenedTasks.length;\n const completed = p.flattenedTasks.filter(t => t.completed).length;\n return (completed / total) * 100;\n });\n\n const avgCompletionRate = completionRates.length > 0\n ? Math.round(completionRates.reduce((sum, rate) => sum + rate, 0) / completionRates.length)\n : 0;\n\n const projectsWithMostTasks = computeTopItems(\n effectivelyActiveProjects.map(p => ({ name: p.name, taskCount: p.flattenedTasks.length })),\n 'taskCount'\n );\n\n const projectsWithMostRemaining = computeTopItems(\n effectivelyActiveProjects\n .map(p => ({ name: p.name, remainingCount: p.flattenedTasks.filter(t => !t.completed).length }))\n .filter(p => p.remainingCount > 0),\n 'remainingCount'\n );\n\n return JSON.stringify({\n totalProjects: allProjects.length,\n activeProjects: activeProjects.length,\n onHoldProjects: onHoldProjects.length,\n droppedProjects: droppedProjects.length,\n doneProjects: doneProjects.length,\n sequentialProjects: sequentialProjects.length,\n parallelProjects: parallelProjects.length,\n avgTasksPerProject,\n avgRemainingPerProject,\n avgCompletionRate,\n projectsWithMostTasks,\n projectsWithMostRemaining\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async listFolders(filters: FolderFilters = {}): Promise<Folder[]> {\n const includeDropped = filters.includeDropped ?? false;\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const includeDropped = ${includeDropped};\n const results = [];\n for (const folder of folders) {\n if (!includeDropped && !folder.effectiveActive) continue;\n results.push(serializeFolder(folder, includeDropped));\n }\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getFolder(idOrName: string, filters: FolderFilters = {}): Promise<Folder> {\n const includeDropped = filters.includeDropped ?? false;\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const includeDropped = ${includeDropped};\n\n function findFolder(idOrName) {\n for (const folder of flattenedFolders) {\n if (folder.id.primaryKey === idOrName || folder.name === idOrName) {\n return folder;\n }\n }\n throw new Error(\"Folder not found: \" + idOrName);\n }\n\n const folder = findFolder(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeFolder(folder, includeDropped));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n}\n","import dayjs from 'dayjs';\nimport { OmniFocusCliError } from './errors.js';\n\nexport function parseDateTime(input: string): string {\n const d = dayjs(input);\n if (!d.isValid()) {\n throw new OmniFocusCliError(`Invalid date: ${input}`, 400);\n }\n return d.toISOString();\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { ProjectFilters, UpdateProjectOptions } from '../types.js';\n\nexport function createProjectCommand(): Command {\n const command = new Command('project');\n command.description('Manage OmniFocus projects');\n\n command\n .command('list')\n .alias('ls')\n .description('List projects')\n .option('-f, --folder <name>', 'Filter by folder')\n .option('-s, --status <status>', 'Filter by status (active, on hold, dropped)')\n .option('-d, --dropped', 'Include dropped projects')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const filters: ProjectFilters = {\n includeDropped: options.dropped,\n ...(options.folder && { folder: options.folder }),\n ...(options.status && { status: options.status }),\n };\n const projects = await of.listProjects(filters);\n outputJson(projects);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new project')\n .option('-f, --folder <name>', 'Assign to folder')\n .option('--note <text>', 'Add note')\n .option('-t, --tag <tags...>', 'Add tags')\n .option('-s, --sequential', 'Make it a sequential project')\n .option('--status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const project = await of.createProject({\n name,\n note: options.note,\n folder: options.folder,\n tags: options.tag,\n sequential: options.sequential,\n status: options.status,\n });\n outputJson(project);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing project')\n .option('-n, --name <name>', 'Rename project')\n .option('--note <text>', 'New note')\n .option('-f, --folder <name>', 'Move to folder')\n .option('-t, --tag <tags...>', 'Replace tags')\n .option('-s, --sequential', 'Make it sequential')\n .option('-p, --parallel', 'Make it parallel')\n .option('--status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateProjectOptions = {\n ...(options.name && { name: options.name }),\n ...(options.note !== undefined && { note: options.note }),\n ...(options.folder && { folder: options.folder }),\n ...(options.tag && { tags: options.tag }),\n ...(options.sequential && { sequential: true }),\n ...(options.parallel && { sequential: false }),\n ...(options.status && { status: options.status }),\n };\n const project = await of.updateProject(idOrName, updates);\n outputJson(project);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a project')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteProject(idOrName);\n outputJson({ message: 'Project deleted successfully' });\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View project details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const project = await of.getProject(idOrName);\n outputJson(project);\n })\n );\n\n command\n .command('stats')\n .description('Show project statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getProjectStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\n\nexport function createInboxCommand(): Command {\n const command = new Command('inbox');\n command.description('Manage OmniFocus inbox');\n\n command\n .command('list')\n .alias('ls')\n .description('List inbox tasks')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const tasks = await of.listInboxTasks();\n outputJson(tasks);\n })\n );\n\n command\n .command('count')\n .description('Get inbox count')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const count = await of.getInboxCount();\n outputJson({ count });\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\n\nexport function createSearchCommand(): Command {\n const command = new Command('search');\n command.description('Search tasks by name or note');\n command.argument('<query>', 'Search query');\n\n command.action(\n withErrorHandling(async (query) => {\n const of = new OmniFocus();\n const tasks = await of.searchTasks(query);\n outputJson(tasks);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\n\nexport function createPerspectiveCommand(): Command {\n const command = new Command('perspective');\n command.description('Manage OmniFocus perspectives');\n\n command\n .command('list')\n .alias('ls')\n .description('List all perspectives')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const perspectives = await of.listPerspectives();\n outputJson(perspectives);\n })\n );\n\n command\n .command('view <name>')\n .description('View tasks in a perspective')\n .action(\n withErrorHandling(async (name) => {\n const of = new OmniFocus();\n const tasks = await of.getPerspectiveTasks(name);\n outputJson(tasks);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { UpdateTagOptions } from '../types.js';\n\nexport function createTagCommand(): Command {\n const command = new Command('tag');\n command.description('Manage and analyze OmniFocus tags');\n\n command\n .command('list')\n .alias('ls')\n .description('List tags with usage information')\n .option('-u, --unused-days <days>', 'Show tags unused for N days', parseInt)\n .option('-s, --sort <field>', 'Sort by: name, usage, activity (default: name)', 'name')\n .option('-a, --active-only', 'Only count active (incomplete) tasks')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const tags = await of.listTags({\n unusedDays: options.unusedDays,\n sortBy: options.sort,\n activeOnly: options.activeOnly,\n });\n outputJson(tags);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new tag')\n .option('-p, --parent <name>', 'Create as child of parent tag')\n .option('-s, --status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const tag = await of.createTag({\n name,\n parent: options.parent,\n status: options.status,\n });\n outputJson(tag);\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View tag details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const tag = await of.getTag(idOrName);\n outputJson(tag);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing tag')\n .option('-n, --name <name>', 'Rename tag')\n .option('-s, --status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateTagOptions = {\n ...(options.name && { name: options.name }),\n ...(options.status && { status: options.status }),\n };\n const tag = await of.updateTag(idOrName, updates);\n outputJson(tag);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a tag')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteTag(idOrName);\n outputJson({ message: 'Tag deleted successfully' });\n })\n );\n\n command\n .command('stats')\n .description('Show tag usage statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getTagStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { FolderFilters } from '../types.js';\n\nexport function createFolderCommand(): Command {\n const command = new Command('folder');\n command.description('View OmniFocus folder hierarchy');\n\n command\n .command('list')\n .alias('ls')\n .description('List top-level folders with nested children')\n .option('-d, --dropped', 'Include dropped folders')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const folders = await of.listFolders({ includeDropped: options.dropped });\n outputJson(folders);\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View folder details and children')\n .option('-d, --dropped', 'Include dropped child folders')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const filters: FolderFilters = { includeDropped: options.dropped };\n const folder = await of.getFolder(idOrName, filters);\n outputJson(folder);\n })\n );\n\n return command;\n}\n"],"mappings":"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","names":["Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/lib/output.ts","../src/commands/task.ts","../src/lib/errors.ts","../src/lib/command-utils.ts","../src/lib/omnifocus.ts","../src/lib/dates.ts","../src/commands/project.ts","../src/commands/inbox.ts","../src/commands/search.ts","../src/commands/perspective.ts","../src/commands/tag.ts","../src/commands/folder.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { Command } from 'commander';\nimport { setOutputOptions } from './lib/output.js';\nimport { createTaskCommand } from './commands/task.js';\nimport { createProjectCommand } from './commands/project.js';\nimport { createInboxCommand } from './commands/inbox.js';\nimport { createSearchCommand } from './commands/search.js';\nimport { createPerspectiveCommand } from './commands/perspective.js';\nimport { createTagCommand } from './commands/tag.js';\nimport { createFolderCommand } from './commands/folder.js';\n\nconst program = new Command();\n\nprogram\n .name('of')\n .description('A command-line interface for OmniFocus on macOS')\n .version(__VERSION__)\n .option('-c, --compact', 'Minified JSON output (single line)')\n .hook('preAction', (thisCommand) => {\n const options = thisCommand.opts();\n setOutputOptions({\n compact: options.compact,\n });\n });\n\nprogram.addCommand(createTaskCommand());\nprogram.addCommand(createProjectCommand());\nprogram.addCommand(createInboxCommand());\nprogram.addCommand(createSearchCommand());\nprogram.addCommand(createPerspectiveCommand());\nprogram.addCommand(createTagCommand());\nprogram.addCommand(createFolderCommand());\n\nprogram.parseAsync().catch(() => {\n process.exit(1);\n});\n","export interface OutputOptions {\n compact?: boolean;\n}\n\nlet globalOutputOptions: OutputOptions = {};\n\nexport function setOutputOptions(options: OutputOptions): void {\n globalOutputOptions = options;\n}\n\nexport function outputJson(data: unknown, options: OutputOptions = {}): void {\n const mergedOptions = { ...globalOutputOptions, ...options };\n const jsonString = mergedOptions.compact ? JSON.stringify(data) : JSON.stringify(data, null, 2);\n\n console.log(jsonString);\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport { parseDateTime } from '../lib/dates.js';\nimport type { TaskFilters, UpdateTaskOptions } from '../types.js';\n\nexport function createTaskCommand(): Command {\n const command = new Command('task');\n command.description('Manage OmniFocus tasks');\n\n command\n .command('list')\n .alias('ls')\n .description('List tasks')\n .option('-f, --flagged', 'Show only flagged tasks')\n .option('-p, --project <name>', 'Filter by project')\n .option('-t, --tag <name>', 'Filter by tag')\n .option('-c, --completed', 'Include completed tasks')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const filters: TaskFilters = {\n includeCompleted: options.completed,\n ...(options.flagged && { flagged: true }),\n ...(options.project && { project: options.project }),\n ...(options.tag && { tag: options.tag }),\n };\n const tasks = await of.listTasks(filters);\n outputJson(tasks);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new task')\n .option('-p, --project <name>', 'Assign to project')\n .option('--note <text>', 'Add note')\n .option('-t, --tag <tags...>', 'Add tags')\n .option('-d, --due <date>', 'Set due date')\n .option('-D, --defer <date>', 'Set defer date')\n .option('-f, --flagged', 'Flag the task')\n .option('-e, --estimate <minutes>', 'Estimated time in minutes', parseInt)\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const task = await of.createTask({\n name,\n note: options.note,\n project: options.project,\n tags: options.tag,\n due: options.due ? parseDateTime(options.due) : undefined,\n defer: options.defer ? parseDateTime(options.defer) : undefined,\n flagged: options.flagged,\n estimatedMinutes: options.estimate,\n });\n outputJson(task);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing task')\n .option('-n, --name <name>', 'New name')\n .option('--note <text>', 'New note')\n .option('-p, --project <name>', 'Move to project')\n .option('-t, --tag <tags...>', 'Replace tags')\n .option('-d, --due <date>', 'Set due date')\n .option('-D, --defer <date>', 'Set defer date')\n .option('-f, --flag', 'Flag the task')\n .option('-F, --unflag', 'Unflag the task')\n .option('-c, --complete', 'Mark as completed')\n .option('-C, --incomplete', 'Mark as incomplete')\n .option('-e, --estimate <minutes>', 'Estimated time in minutes', parseInt)\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateTaskOptions = {\n ...(options.name && { name: options.name }),\n ...(options.note !== undefined && { note: options.note }),\n ...(options.project && { project: options.project }),\n ...(options.tag && { tags: options.tag }),\n ...(options.due !== undefined && {\n due: options.due ? parseDateTime(options.due) : null,\n }),\n ...(options.defer !== undefined && {\n defer: options.defer ? parseDateTime(options.defer) : null,\n }),\n ...(options.flag && { flagged: true }),\n ...(options.unflag && { flagged: false }),\n ...(options.complete && { completed: true }),\n ...(options.incomplete && { completed: false }),\n ...(options.estimate !== undefined && { estimatedMinutes: options.estimate }),\n };\n const task = await of.updateTask(idOrName, updates);\n outputJson(task);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a task')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteTask(idOrName);\n outputJson({ message: 'Task deleted successfully' });\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View task details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const task = await of.getTask(idOrName);\n outputJson(task);\n })\n );\n\n command\n .command('stats')\n .description('Show task statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getTaskStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { outputJson } from './output.js';\n\nexport class OmniFocusCliError extends Error {\n constructor(\n message: string,\n public statusCode: number = 500\n ) {\n super(message);\n this.name = 'OmniFocusCliError';\n }\n}\n\nexport function handleError(error: unknown): never {\n let name = 'unknown_error';\n let detail = 'An unknown error occurred';\n let statusCode = 500;\n\n if (error instanceof OmniFocusCliError) {\n name = 'cli_error';\n detail = error.message;\n statusCode = error.statusCode;\n } else if (error instanceof Error) {\n name = 'omnifocus_error';\n detail = error.message;\n\n if (detail.includes('not found')) {\n statusCode = 404;\n } else if (detail.includes('Multiple')) {\n statusCode = 400;\n }\n }\n\n outputJson({ error: { name, detail, statusCode } });\n process.exit(1);\n}\n","import { handleError } from './errors.js';\n\nexport function withErrorHandling<T extends unknown[], R>(\n fn: (...args: T) => Promise<R>\n): (...args: T) => Promise<void> {\n return async (...args: T) => {\n try {\n await fn(...args);\n } catch (error) {\n handleError(error);\n }\n };\n}\n","import { execFile } from 'child_process';\nimport { writeFile, unlink } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\nimport { promisify } from 'util';\nimport type {\n Task,\n Project,\n TaskFilters,\n ProjectFilters,\n CreateTaskOptions,\n UpdateTaskOptions,\n CreateProjectOptions,\n UpdateProjectOptions,\n Perspective,\n Tag,\n TagListOptions,\n TagStats,\n TaskStats,\n ProjectStats,\n CreateTagOptions,\n UpdateTagOptions,\n Folder,\n FolderFilters,\n} from '../types.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport class OmniFocus {\n private readonly PROJECT_STATUS_MAP = {\n active: 'Active',\n 'on hold': 'OnHold',\n dropped: 'Dropped',\n } as const;\n\n private readonly OMNI_HELPERS = `\n function serializeTask(task) {\n const containingProject = task.containingProject;\n const tagNames = task.tags.map(t => t.name);\n\n return {\n id: task.id.primaryKey,\n name: task.name,\n note: task.note || null,\n completed: task.completed,\n dropped: task.dropped,\n effectivelyActive: task.effectiveActive,\n flagged: task.flagged,\n project: containingProject ? containingProject.name : null,\n tags: tagNames,\n defer: task.deferDate ? task.deferDate.toISOString() : null,\n due: task.dueDate ? task.dueDate.toISOString() : null,\n estimatedMinutes: task.estimatedMinutes || null,\n completionDate: task.completionDate ? task.completionDate.toISOString() : null,\n added: task.added ? task.added.toISOString() : null,\n modified: task.modified ? task.modified.toISOString() : null\n };\n }\n\n function serializeProject(project) {\n const parentFolder = project.parentFolder;\n const allTasks = project.flattenedTasks;\n const remainingTasks = allTasks.filter(t => !t.completed);\n const tagNames = project.tags.map(t => t.name);\n\n return {\n id: project.id.primaryKey,\n name: project.name,\n note: project.note || null,\n status: projectStatusToString(project.status),\n folder: parentFolder ? parentFolder.name : null,\n sequential: project.sequential,\n taskCount: allTasks.length,\n remainingCount: remainingTasks.length,\n tags: tagNames\n };\n }\n\n function findTask(idOrName) {\n for (const task of flattenedTasks) {\n if (task.id.primaryKey === idOrName || task.name === idOrName) {\n return task;\n }\n }\n throw new Error(\"Task not found: \" + idOrName);\n }\n\n function findProject(idOrName) {\n for (const project of flattenedProjects) {\n if (project.id.primaryKey === idOrName || project.name === idOrName) {\n return project;\n }\n }\n throw new Error(\"Project not found: \" + idOrName);\n }\n\n function getTagPath(tag) {\n const parts = [tag.name];\n let current = tag.parent;\n while (current) {\n parts.unshift(current.name);\n current = current.parent;\n }\n return parts.join('/');\n }\n\n function findTag(idOrName) {\n for (const tag of flattenedTags) {\n if (tag.id.primaryKey === idOrName) {\n return tag;\n }\n }\n\n if (idOrName.includes('/')) {\n for (const tag of flattenedTags) {\n if (getTagPath(tag) === idOrName) {\n return tag;\n }\n }\n throw new Error(\"Tag not found: \" + idOrName);\n }\n\n const matches = flattenedTags.filter(tag => tag.name === idOrName);\n\n if (matches.length === 0) {\n throw new Error(\"Tag not found: \" + idOrName);\n }\n\n if (matches.length > 1) {\n const paths = matches.map(getTagPath);\n throw new Error(\"Multiple tags found with name '\" + idOrName + \"'. Please use full path:\\\\n \" + paths.join('\\\\n ') + \"\\\\nOr use tag ID: \" + matches.map(t => t.id.primaryKey).join(', '));\n }\n\n return matches[0];\n }\n\n function findByName(collection, name, typeName) {\n for (const item of collection) {\n if (item.name === name) {\n return item;\n }\n }\n throw new Error(typeName + \" not found: \" + name);\n }\n\n function assignTags(target, tagNames) {\n for (const tagName of tagNames) {\n const tag = findTag(tagName);\n target.addTag(tag);\n }\n }\n\n function replaceTagsOn(target, tagNames) {\n target.clearTags();\n assignTags(target, tagNames);\n }\n\n function statusToString(status, StatusEnum) {\n if (status === StatusEnum.Active) return 'active';\n if (status === StatusEnum.OnHold) return 'on hold';\n if (status === StatusEnum.Dropped) return 'dropped';\n if (status === StatusEnum.Done) return 'done';\n return 'dropped';\n }\n\n function stringToStatus(str, StatusEnum) {\n if (str === 'active') return StatusEnum.Active;\n if (str === 'on hold') return StatusEnum.OnHold;\n return StatusEnum.Dropped;\n }\n\n const projectStatusToString = (status) => statusToString(status, Project.Status);\n const tagStatusToString = (status) => statusToString(status, Tag.Status);\n const folderStatusToString = (status) => {\n if (status === Folder.Status.Active) return 'active';\n return 'dropped';\n };\n const stringToProjectStatus = (str) => stringToStatus(str, Project.Status);\n const stringToTagStatus = (str) => stringToStatus(str, Tag.Status);\n\n function serializeFolder(folder, includeDropped = false) {\n let childFolders = folder.folders;\n if (!includeDropped) {\n childFolders = childFolders.filter(c => c.effectiveActive);\n }\n\n return {\n id: folder.id.primaryKey,\n name: folder.name,\n status: folderStatusToString(folder.status),\n effectivelyActive: folder.effectiveActive,\n parent: folder.parent ? folder.parent.name : null,\n projectCount: folder.projects.length,\n remainingProjectCount: folder.projects.filter(p => p.effectiveActive).length,\n folderCount: folder.folders.length,\n children: childFolders.map(child => serializeFolder(child, includeDropped))\n };\n }\n\n function computeTopItems(items, keyFn, topN = 5) {\n return items\n .sort((a, b) => b[keyFn] - a[keyFn])\n .slice(0, topN)\n .map(item => ({ name: item.name, [keyFn]: item[keyFn] }));\n }\n\n function computeAverage(total, count) {\n return count > 0 ? Math.round((total / count) * 10) / 10 : 0;\n }\n\n function serializeTag(tag, activeOnly = false) {\n const tasks = tag.tasks;\n const remainingTasks = tag.remainingTasks;\n const includedTasks = activeOnly ? remainingTasks : tasks;\n\n const dates = [];\n if (tag.added) dates.push(tag.added);\n if (tag.modified) dates.push(tag.modified);\n\n for (const task of includedTasks) {\n if (task.added) dates.push(task.added);\n if (task.modified) dates.push(task.modified);\n if (!activeOnly && task.completionDate) dates.push(task.completionDate);\n if (!activeOnly && task.effectiveCompletionDate) dates.push(task.effectiveCompletionDate);\n }\n\n const lastActivity = dates.length > 0\n ? dates.reduce((latest, current) => current > latest ? current : latest)\n : null;\n\n return {\n id: tag.id.primaryKey,\n name: tag.name,\n taskCount: includedTasks.length,\n remainingTaskCount: remainingTasks.length,\n added: tag.added ? tag.added.toISOString() : null,\n modified: tag.modified ? tag.modified.toISOString() : null,\n lastActivity: lastActivity ? lastActivity.toISOString() : null,\n active: tag.active,\n status: tagStatusToString(tag.status),\n parent: tag.parent ? tag.parent.name : null,\n children: tag.children.map(c => c.name),\n allowsNextAction: tag.allowsNextAction\n };\n }\n `;\n\n private async executeJXA(script: string, timeoutMs = 30000): Promise<string> {\n const tmpFile = join(tmpdir(), `omnifocus-${Date.now()}.js`);\n\n try {\n await writeFile(tmpFile, script, 'utf-8');\n\n const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', tmpFile], {\n timeout: timeoutMs,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return stdout.trim();\n } finally {\n try {\n await unlink(tmpFile);\n } catch {\n /* ignore cleanup errors */\n }\n }\n }\n\n private escapeString(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n }\n\n private wrapOmniScript(omniScript: string): string {\n return `\n const app = Application('OmniFocus');\n app.includeStandardAdditions = true;\n const result = app.evaluateJavascript(${JSON.stringify(omniScript.trim())});\n result;\n `.trim();\n }\n\n private buildTaskFilters(filters: TaskFilters): string {\n const conditions: string[] = [];\n\n if (!filters.includeCompleted) {\n conditions.push('if (task.completed) continue;');\n }\n if (!filters.includeDropped) {\n conditions.push('if (!task.effectiveActive) continue;');\n }\n if (filters.flagged) {\n conditions.push('if (!task.flagged) continue;');\n conditions.push('if (task.taskStatus !== Task.Status.Available) continue;');\n }\n if (filters.project) {\n conditions.push(`\n if (!task.containingProject || task.containingProject.name !== \"${this.escapeString(filters.project)}\") {\n continue;\n }\n `);\n }\n if (filters.tag) {\n conditions.push(`\n if (!task.tags.some(t => t.name === \"${this.escapeString(filters.tag)}\")) {\n continue;\n }\n `);\n }\n\n return conditions.join('\\n ');\n }\n\n private buildProjectFilters(filters: ProjectFilters): string {\n const conditions: string[] = [];\n\n if (!filters.includeDropped) {\n conditions.push(\n 'if (project.status === Project.Status.Dropped || project.status === Project.Status.Done) continue;'\n );\n conditions.push(\n 'if (project.parentFolder && !project.parentFolder.effectiveActive) continue;'\n );\n }\n if (filters.status) {\n const statusCheck = this.PROJECT_STATUS_MAP[filters.status];\n conditions.push(`if (project.status !== Project.Status.${statusCheck}) continue;`);\n }\n if (filters.folder) {\n conditions.push(\n `if (!project.parentFolder || project.parentFolder.name !== \"${this.escapeString(filters.folder)}\") continue;`\n );\n }\n\n return conditions.join('\\n ');\n }\n\n private buildTaskUpdates(options: UpdateTaskOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`task.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.note !== undefined) {\n updates.push(`task.note = \"${this.escapeString(options.note)}\";`);\n }\n if (options.flagged !== undefined) {\n updates.push(`task.flagged = ${options.flagged};`);\n }\n if (options.completed !== undefined) {\n updates.push(options.completed ? 'task.markComplete();' : 'task.markIncomplete();');\n }\n if (options.estimatedMinutes !== undefined) {\n updates.push(`task.estimatedMinutes = ${options.estimatedMinutes};`);\n }\n if (options.defer !== undefined) {\n updates.push(\n options.defer\n ? `task.deferDate = new Date(${JSON.stringify(options.defer)});`\n : 'task.deferDate = null;'\n );\n }\n if (options.due !== undefined) {\n updates.push(\n options.due\n ? `task.dueDate = new Date(${JSON.stringify(options.due)});`\n : 'task.dueDate = null;'\n );\n }\n if (options.project !== undefined && options.project) {\n updates.push(`\n const targetProject = findByName(flattenedProjects, \"${this.escapeString(options.project)}\", \"Project\");\n moveTasks([task], targetProject);\n `);\n }\n if (options.tags !== undefined) {\n updates.push(`replaceTagsOn(task, ${JSON.stringify(options.tags)});`);\n }\n\n return updates.join('\\n ');\n }\n\n private buildTagUpdates(options: UpdateTagOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`tag.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.status !== undefined) {\n updates.push(`tag.status = stringToTagStatus(\"${options.status}\");`);\n }\n\n return updates.join('\\n ');\n }\n\n private buildProjectUpdates(options: UpdateProjectOptions): string {\n const updates: string[] = [];\n\n if (options.name !== undefined) {\n updates.push(`project.name = \"${this.escapeString(options.name)}\";`);\n }\n if (options.note !== undefined) {\n updates.push(`project.note = \"${this.escapeString(options.note)}\";`);\n }\n if (options.sequential !== undefined) {\n updates.push(`project.sequential = ${options.sequential};`);\n }\n if (options.status !== undefined) {\n updates.push(`project.status = stringToProjectStatus(\"${options.status}\");`);\n }\n if (options.folder !== undefined && options.folder) {\n updates.push(`\n const targetFolder = findByName(flattenedFolders, \"${this.escapeString(options.folder)}\", \"Folder\");\n moveProjects([project], targetFolder);\n `);\n }\n if (options.tags !== undefined) {\n updates.push(`replaceTagsOn(project, ${JSON.stringify(options.tags)});`);\n }\n\n return updates.join('\\n ');\n }\n\n async listTasks(filters: TaskFilters = {}): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n for (const task of flattenedTasks) {\n ${this.buildTaskFilters(filters)}\n results.push(serializeTask(task));\n }\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createTask(options: CreateTaskOptions): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.project\n ? `const targetProject = findByName(flattenedProjects, \"${this.escapeString(options.project)}\", \"Project\");\n const task = new Task(\"${this.escapeString(options.name)}\", targetProject);`\n : `const task = new Task(\"${this.escapeString(options.name)}\");`\n }\n\n ${options.note ? `task.note = \"${this.escapeString(options.note)}\";` : ''}\n ${options.flagged ? 'task.flagged = true;' : ''}\n ${options.estimatedMinutes ? `task.estimatedMinutes = ${options.estimatedMinutes};` : ''}\n ${options.defer ? `task.deferDate = new Date(${JSON.stringify(options.defer)});` : ''}\n ${options.due ? `task.dueDate = new Date(${JSON.stringify(options.due)});` : ''}\n ${options.tags && options.tags.length > 0 ? `assignTags(task, ${JSON.stringify(options.tags)});` : ''}\n\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateTask(idOrName: string, options: UpdateTaskOptions): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const task = findTask(\"${this.escapeString(idOrName)}\");\n ${this.buildTaskUpdates(options)}\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteTask(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findTask(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async listProjects(filters: ProjectFilters = {}): Promise<Project[]> {\n const filterCode = this.buildProjectFilters(filters);\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n for (const project of flattenedProjects) {\n ${filterCode}\n results.push(serializeProject(project));\n }\n return JSON.stringify(results);\n })();\n `;\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createProject(options: CreateProjectOptions): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.folder\n ? `const targetFolder = findByName(flattenedFolders, \"${this.escapeString(options.folder)}\", \"Folder\");\n const project = new Project(\"${this.escapeString(options.name)}\", targetFolder);`\n : `const project = new Project(\"${this.escapeString(options.name)}\");`\n }\n\n ${options.note ? `project.note = \"${this.escapeString(options.note)}\";` : ''}\n ${options.sequential !== undefined ? `project.sequential = ${options.sequential};` : ''}\n ${options.status ? `project.status = stringToProjectStatus(\"${options.status}\");` : ''}\n ${options.tags && options.tags.length > 0 ? `assignTags(project, ${JSON.stringify(options.tags)});` : ''}\n\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateProject(idOrName: string, options: UpdateProjectOptions): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const project = findProject(\"${this.escapeString(idOrName)}\");\n ${this.buildProjectUpdates(options)}\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteProject(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findProject(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async listInboxTasks(): Promise<Task[]> {\n return this.getPerspectiveTasks('Inbox');\n }\n\n async getInboxCount(): Promise<number> {\n const tasks = await this.getPerspectiveTasks('Inbox');\n return tasks.length;\n }\n\n async searchTasks(query: string): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n const searchQuery = \"${this.escapeString(query)}\".toLowerCase();\n\n for (const task of flattenedTasks) {\n if (task.completed) continue;\n if (!task.effectiveActive) continue;\n\n const name = task.name.toLowerCase();\n const note = (task.note || '').toLowerCase();\n\n if (name.includes(searchQuery) || note.includes(searchQuery)) {\n results.push(serializeTask(task));\n }\n }\n\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getTask(idOrName: string): Promise<Task> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const task = findTask(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeTask(task));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getProject(idOrName: string): Promise<Project> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const project = findProject(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeProject(project));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async listPerspectives(): Promise<Perspective[]> {\n const omniScript = `\n (() => {\n const results = [];\n\n const builtInNames = ['Inbox', 'Flagged', 'Forecast', 'Projects', 'Tags', 'Nearby', 'Review'];\n for (const name of builtInNames) {\n results.push({ id: name, name: name });\n }\n\n const customPerspectives = Perspective.Custom.all;\n for (const perspective of customPerspectives) {\n results.push({ id: perspective.name, name: perspective.name });\n }\n\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getPerspectiveTasks(perspectiveName: string): Promise<Task[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const doc = document;\n const windows = doc.windows;\n\n if (windows.length === 0) {\n throw new Error(\"No OmniFocus window is open. Please open an OmniFocus window and try again.\");\n }\n\n const win = windows[0];\n const perspectiveName = \"${this.escapeString(perspectiveName)}\";\n\n const builtInPerspectives = {\n 'inbox': Perspective.BuiltIn.Inbox,\n 'flagged': Perspective.BuiltIn.Flagged,\n 'forecast': Perspective.BuiltIn.Forecast,\n 'projects': Perspective.BuiltIn.Projects,\n 'tags': Perspective.BuiltIn.Tags,\n 'nearby': Perspective.BuiltIn.Nearby,\n 'review': Perspective.BuiltIn.Review\n };\n\n const lowerName = perspectiveName.toLowerCase();\n if (builtInPerspectives[lowerName]) {\n win.perspective = builtInPerspectives[lowerName];\n } else {\n const customPerspective = Perspective.Custom.byName(perspectiveName);\n if (customPerspective) {\n win.perspective = customPerspective;\n } else {\n throw new Error(\"Perspective not found: \" + perspectiveName);\n }\n }\n\n const content = win.content;\n if (!content) {\n throw new Error(\"No content available in window\");\n }\n\n const tasks = [];\n content.rootNode.apply(node => {\n const obj = node.object;\n if (obj instanceof Task) {\n tasks.push(serializeTask(obj));\n }\n });\n\n return JSON.stringify(tasks);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript), 60000);\n return JSON.parse(output);\n }\n\n async listTags(options: TagListOptions = {}): Promise<Tag[]> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const results = [];\n const now = new Date();\n const activeOnly = ${!!options.activeOnly};\n\n for (const tag of flattenedTags) {\n const serialized = serializeTag(tag, activeOnly);\n results.push(serialized);\n }\n\n ${\n options.unusedDays\n ? `\n const cutoffDate = new Date(now.getTime() - (${options.unusedDays} * 24 * 60 * 60 * 1000));\n const filtered = results.filter(tag => {\n if (!tag.lastActivity) return true;\n return new Date(tag.lastActivity) < cutoffDate;\n });\n return JSON.stringify(filtered);\n `\n : 'return JSON.stringify(results);'\n }\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n const tags = JSON.parse(output);\n\n return this.sortTags(tags, options.sortBy);\n }\n\n private sortTags(tags: Tag[], sortBy: string = 'name'): Tag[] {\n const sortFns: Record<string, (a: Tag, b: Tag) => number> = {\n usage: (a, b) => b.taskCount - a.taskCount,\n activity: (a, b) => {\n if (!a.lastActivity && !b.lastActivity) return 0;\n if (!a.lastActivity) return 1;\n if (!b.lastActivity) return -1;\n return new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime();\n },\n name: (a, b) => a.name.localeCompare(b.name),\n };\n\n return tags.sort(sortFns[sortBy] || sortFns.name);\n }\n\n async getTagStats(): Promise<TagStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allTags = [];\n for (const tag of flattenedTags) {\n allTags.push(serializeTag(tag));\n }\n\n const activeTags = allTags.filter(t => t.active);\n const tagsWithTasks = allTags.filter(t => t.taskCount > 0);\n const unusedTags = allTags.filter(t => t.taskCount === 0);\n\n const totalTasks = tagsWithTasks.reduce((sum, t) => sum + t.taskCount, 0);\n const avgTasksPerTag = computeAverage(totalTasks, tagsWithTasks.length);\n\n const mostUsedTags = computeTopItems(allTags, 'taskCount');\n const leastUsedTags = computeTopItems(\n tagsWithTasks.map(t => ({ ...t, taskCount: -t.taskCount })),\n 'taskCount'\n ).map(t => ({ name: t.name, taskCount: -t.taskCount }));\n\n const now = new Date();\n const thirtyDaysAgo = new Date(now.getTime() - (30 * 24 * 60 * 60 * 1000));\n const staleTags = allTags\n .filter(t => t.lastActivity && new Date(t.lastActivity) < thirtyDaysAgo)\n .map(t => ({\n name: t.name,\n daysSinceActivity: Math.floor((now - new Date(t.lastActivity)) / (24 * 60 * 60 * 1000))\n }))\n .sort((a, b) => b.daysSinceActivity - a.daysSinceActivity);\n\n return JSON.stringify({\n totalTags: allTags.length,\n activeTags: activeTags.length,\n tagsWithTasks: tagsWithTasks.length,\n unusedTags: unusedTags.length,\n avgTasksPerTag,\n mostUsedTags,\n leastUsedTags,\n staleTags\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async createTag(options: CreateTagOptions): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n ${\n options.parent\n ? `const parentTag = findTag(\"${this.escapeString(options.parent)}\");\n const tag = new Tag(\"${this.escapeString(options.name)}\", parentTag);`\n : `const tag = new Tag(\"${this.escapeString(options.name)}\", tags.beginning);`\n }\n\n ${options.status ? `tag.status = stringToTagStatus(\"${options.status}\");` : ''}\n\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getTag(idOrName: string): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const tag = findTag(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async updateTag(idOrName: string, options: UpdateTagOptions): Promise<Tag> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const tag = findTag(\"${this.escapeString(idOrName)}\");\n ${this.buildTagUpdates(options)}\n return JSON.stringify(serializeTag(tag));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async deleteTag(idOrName: string): Promise<void> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n deleteObject(findTag(\"${this.escapeString(idOrName)}\"));\n })();\n `;\n\n await this.executeJXA(this.wrapOmniScript(omniScript));\n }\n\n async getTaskStats(): Promise<TaskStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allTasks = Array.from(flattenedTasks);\n const now = new Date();\n\n const activeTasks = allTasks.filter(t => !t.completed && t.effectiveActive);\n const completedTasks = allTasks.filter(t => t.completed);\n const flaggedTasks = activeTasks.filter(t => t.flagged);\n const overdueActiveTasks = activeTasks.filter(t => t.dueDate && t.dueDate < now);\n\n const tasksWithEstimates = allTasks.filter(t => t.estimatedMinutes && t.estimatedMinutes > 0);\n const totalEstimatedMinutes = tasksWithEstimates.reduce((sum, t) => sum + (t.estimatedMinutes || 0), 0);\n const avgEstimatedMinutes = tasksWithEstimates.length > 0\n ? Math.round(totalEstimatedMinutes / tasksWithEstimates.length)\n : null;\n\n const totalNonDropped = allTasks.filter(t => t.effectiveActive || t.completed).length;\n const completionRate = totalNonDropped > 0\n ? Math.round((completedTasks.length / totalNonDropped) * 100)\n : 0;\n\n const projectCounts = {};\n for (const task of allTasks) {\n if (!task.effectiveActive && !task.completed) continue;\n const projectName = task.containingProject ? task.containingProject.name : 'Inbox';\n projectCounts[projectName] = (projectCounts[projectName] || 0) + 1;\n }\n const tasksByProject = computeTopItems(\n Object.entries(projectCounts).map(([name, count]) => ({ name, taskCount: count })),\n 'taskCount'\n );\n\n const tagCounts = {};\n for (const task of allTasks) {\n if (!task.effectiveActive && !task.completed) continue;\n for (const tag of task.tags) {\n tagCounts[tag.name] = (tagCounts[tag.name] || 0) + 1;\n }\n }\n const tasksByTag = computeTopItems(\n Object.entries(tagCounts).map(([name, count]) => ({ name, taskCount: count })),\n 'taskCount'\n );\n\n return JSON.stringify({\n totalTasks: allTasks.length,\n activeTasks: activeTasks.length,\n completedTasks: completedTasks.length,\n flaggedTasks: flaggedTasks.length,\n overdueActiveTasks: overdueActiveTasks.length,\n avgEstimatedMinutes,\n tasksWithEstimates: tasksWithEstimates.length,\n completionRate,\n tasksByProject,\n tasksByTag\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getProjectStats(): Promise<ProjectStats> {\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const allProjects = Array.from(flattenedProjects);\n\n function isProjectEffectivelyActive(p) {\n if (p.status === Project.Status.Dropped || p.status === Project.Status.Done) return false;\n if (p.parentFolder && !p.parentFolder.effectiveActive) return false;\n return true;\n }\n\n const effectivelyActiveProjects = allProjects.filter(isProjectEffectivelyActive);\n const activeProjects = effectivelyActiveProjects.filter(p => p.status === Project.Status.Active);\n const onHoldProjects = effectivelyActiveProjects.filter(p => p.status === Project.Status.OnHold);\n const droppedProjects = allProjects.filter(p => p.status === Project.Status.Dropped);\n const doneProjects = allProjects.filter(p => p.status === Project.Status.Done);\n const sequentialProjects = effectivelyActiveProjects.filter(p => p.sequential);\n const parallelProjects = effectivelyActiveProjects.filter(p => !p.sequential);\n\n const totalTasks = effectivelyActiveProjects.reduce((sum, p) => sum + p.flattenedTasks.length, 0);\n const totalRemaining = effectivelyActiveProjects.reduce((sum, p) => {\n return sum + p.flattenedTasks.filter(t => !t.completed).length;\n }, 0);\n\n const avgTasksPerProject = computeAverage(totalTasks, effectivelyActiveProjects.length);\n const avgRemainingPerProject = computeAverage(totalRemaining, effectivelyActiveProjects.length);\n\n const completionRates = effectivelyActiveProjects\n .filter(p => p.flattenedTasks.length > 0)\n .map(p => {\n const total = p.flattenedTasks.length;\n const completed = p.flattenedTasks.filter(t => t.completed).length;\n return (completed / total) * 100;\n });\n\n const avgCompletionRate = completionRates.length > 0\n ? Math.round(completionRates.reduce((sum, rate) => sum + rate, 0) / completionRates.length)\n : 0;\n\n const projectsWithMostTasks = computeTopItems(\n effectivelyActiveProjects.map(p => ({ name: p.name, taskCount: p.flattenedTasks.length })),\n 'taskCount'\n );\n\n const projectsWithMostRemaining = computeTopItems(\n effectivelyActiveProjects\n .map(p => ({ name: p.name, remainingCount: p.flattenedTasks.filter(t => !t.completed).length }))\n .filter(p => p.remainingCount > 0),\n 'remainingCount'\n );\n\n return JSON.stringify({\n totalProjects: allProjects.length,\n activeProjects: activeProjects.length,\n onHoldProjects: onHoldProjects.length,\n droppedProjects: droppedProjects.length,\n doneProjects: doneProjects.length,\n sequentialProjects: sequentialProjects.length,\n parallelProjects: parallelProjects.length,\n avgTasksPerProject,\n avgRemainingPerProject,\n avgCompletionRate,\n projectsWithMostTasks,\n projectsWithMostRemaining\n });\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async listFolders(filters: FolderFilters = {}): Promise<Folder[]> {\n const includeDropped = filters.includeDropped ?? false;\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const includeDropped = ${includeDropped};\n const results = [];\n for (const folder of folders) {\n if (!includeDropped && !folder.effectiveActive) continue;\n results.push(serializeFolder(folder, includeDropped));\n }\n return JSON.stringify(results);\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n\n async getFolder(idOrName: string, filters: FolderFilters = {}): Promise<Folder> {\n const includeDropped = filters.includeDropped ?? false;\n const omniScript = `\n ${this.OMNI_HELPERS}\n (() => {\n const includeDropped = ${includeDropped};\n\n function findFolder(idOrName) {\n for (const folder of flattenedFolders) {\n if (folder.id.primaryKey === idOrName || folder.name === idOrName) {\n return folder;\n }\n }\n throw new Error(\"Folder not found: \" + idOrName);\n }\n\n const folder = findFolder(\"${this.escapeString(idOrName)}\");\n return JSON.stringify(serializeFolder(folder, includeDropped));\n })();\n `;\n\n const output = await this.executeJXA(this.wrapOmniScript(omniScript));\n return JSON.parse(output);\n }\n}\n","import dayjs from 'dayjs';\nimport { OmniFocusCliError } from './errors.js';\n\nexport function parseDateTime(input: string): string {\n const d = dayjs(input);\n if (!d.isValid()) {\n throw new OmniFocusCliError(`Invalid date: ${input}`, 400);\n }\n return d.toISOString();\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { ProjectFilters, UpdateProjectOptions } from '../types.js';\n\nexport function createProjectCommand(): Command {\n const command = new Command('project');\n command.description('Manage OmniFocus projects');\n\n command\n .command('list')\n .alias('ls')\n .description('List projects')\n .option('-f, --folder <name>', 'Filter by folder')\n .option('-s, --status <status>', 'Filter by status (active, on hold, dropped)')\n .option('-d, --dropped', 'Include dropped projects')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const filters: ProjectFilters = {\n includeDropped: options.dropped,\n ...(options.folder && { folder: options.folder }),\n ...(options.status && { status: options.status }),\n };\n const projects = await of.listProjects(filters);\n outputJson(projects);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new project')\n .option('-f, --folder <name>', 'Assign to folder')\n .option('--note <text>', 'Add note')\n .option('-t, --tag <tags...>', 'Add tags')\n .option('-s, --sequential', 'Make it a sequential project')\n .option('--status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const project = await of.createProject({\n name,\n note: options.note,\n folder: options.folder,\n tags: options.tag,\n sequential: options.sequential,\n status: options.status,\n });\n outputJson(project);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing project')\n .option('-n, --name <name>', 'Rename project')\n .option('--note <text>', 'New note')\n .option('-f, --folder <name>', 'Move to folder')\n .option('-t, --tag <tags...>', 'Replace tags')\n .option('-s, --sequential', 'Make it sequential')\n .option('-p, --parallel', 'Make it parallel')\n .option('--status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateProjectOptions = {\n ...(options.name && { name: options.name }),\n ...(options.note !== undefined && { note: options.note }),\n ...(options.folder && { folder: options.folder }),\n ...(options.tag && { tags: options.tag }),\n ...(options.sequential && { sequential: true }),\n ...(options.parallel && { sequential: false }),\n ...(options.status && { status: options.status }),\n };\n const project = await of.updateProject(idOrName, updates);\n outputJson(project);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a project')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteProject(idOrName);\n outputJson({ message: 'Project deleted successfully' });\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View project details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const project = await of.getProject(idOrName);\n outputJson(project);\n })\n );\n\n command\n .command('stats')\n .description('Show project statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getProjectStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport { parseDateTime } from '../lib/dates.js';\n\nexport function createInboxCommand(): Command {\n const command = new Command('inbox');\n command.description('Manage OmniFocus inbox');\n\n command\n .command('list')\n .alias('ls')\n .description('List inbox tasks')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const tasks = await of.listInboxTasks();\n outputJson(tasks);\n })\n );\n\n command\n .command('count')\n .description('Get inbox count')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const count = await of.getInboxCount();\n outputJson({ count });\n })\n );\n\n command\n .command('add <name>')\n .description('Add a task to inbox')\n .option('--note <text>', 'Add note')\n .option('-t, --tag <tags...>', 'Add tags')\n .option('-d, --due <date>', 'Set due date')\n .option('-D, --defer <date>', 'Set defer date')\n .option('-f, --flagged', 'Flag the task')\n .option('-e, --estimate <minutes>', 'Estimated time in minutes', parseInt)\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const task = await of.createTask({\n name,\n note: options.note,\n tags: options.tag,\n due: options.due ? parseDateTime(options.due) : undefined,\n defer: options.defer ? parseDateTime(options.defer) : undefined,\n flagged: options.flagged,\n estimatedMinutes: options.estimate,\n });\n outputJson(task);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\n\nexport function createSearchCommand(): Command {\n const command = new Command('search');\n command.description('Search tasks by name or note');\n command.argument('<query>', 'Search query');\n\n command.action(\n withErrorHandling(async (query) => {\n const of = new OmniFocus();\n const tasks = await of.searchTasks(query);\n outputJson(tasks);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\n\nexport function createPerspectiveCommand(): Command {\n const command = new Command('perspective');\n command.description('Manage OmniFocus perspectives');\n\n command\n .command('list')\n .alias('ls')\n .description('List all perspectives')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const perspectives = await of.listPerspectives();\n outputJson(perspectives);\n })\n );\n\n command\n .command('view <name>')\n .description('View tasks in a perspective')\n .action(\n withErrorHandling(async (name) => {\n const of = new OmniFocus();\n const tasks = await of.getPerspectiveTasks(name);\n outputJson(tasks);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { UpdateTagOptions } from '../types.js';\n\nexport function createTagCommand(): Command {\n const command = new Command('tag');\n command.description('Manage and analyze OmniFocus tags');\n\n command\n .command('list')\n .alias('ls')\n .description('List tags with usage information')\n .option('-u, --unused-days <days>', 'Show tags unused for N days', parseInt)\n .option('-s, --sort <field>', 'Sort by: name, usage, activity (default: name)', 'name')\n .option('-a, --active-only', 'Only count active (incomplete) tasks')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const tags = await of.listTags({\n unusedDays: options.unusedDays,\n sortBy: options.sort,\n activeOnly: options.activeOnly,\n });\n outputJson(tags);\n })\n );\n\n command\n .command('create <name>')\n .description('Create a new tag')\n .option('-p, --parent <name>', 'Create as child of parent tag')\n .option('-s, --status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (name, options) => {\n const of = new OmniFocus();\n const tag = await of.createTag({\n name,\n parent: options.parent,\n status: options.status,\n });\n outputJson(tag);\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View tag details')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n const tag = await of.getTag(idOrName);\n outputJson(tag);\n })\n );\n\n command\n .command('update <idOrName>')\n .description('Update an existing tag')\n .option('-n, --name <name>', 'Rename tag')\n .option('-s, --status <status>', 'Set status (active, on hold, dropped)')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const updates: UpdateTagOptions = {\n ...(options.name && { name: options.name }),\n ...(options.status && { status: options.status }),\n };\n const tag = await of.updateTag(idOrName, updates);\n outputJson(tag);\n })\n );\n\n command\n .command('delete <idOrName>')\n .alias('rm')\n .description('Delete a tag')\n .action(\n withErrorHandling(async (idOrName) => {\n const of = new OmniFocus();\n await of.deleteTag(idOrName);\n outputJson({ message: 'Tag deleted successfully' });\n })\n );\n\n command\n .command('stats')\n .description('Show tag usage statistics')\n .action(\n withErrorHandling(async () => {\n const of = new OmniFocus();\n const stats = await of.getTagStats();\n outputJson(stats);\n })\n );\n\n return command;\n}\n","import { Command } from 'commander';\nimport { outputJson } from '../lib/output.js';\nimport { withErrorHandling } from '../lib/command-utils.js';\nimport { OmniFocus } from '../lib/omnifocus.js';\nimport type { FolderFilters } from '../types.js';\n\nexport function createFolderCommand(): Command {\n const command = new Command('folder');\n command.description('View OmniFocus folder hierarchy');\n\n command\n .command('list')\n .alias('ls')\n .description('List top-level folders with nested children')\n .option('-d, --dropped', 'Include dropped folders')\n .action(\n withErrorHandling(async (options) => {\n const of = new OmniFocus();\n const folders = await of.listFolders({ includeDropped: options.dropped });\n outputJson(folders);\n })\n );\n\n command\n .command('view <idOrName>')\n .description('View folder details and children')\n .option('-d, --dropped', 'Include dropped child folders')\n .action(\n withErrorHandling(async (idOrName, options) => {\n const of = new OmniFocus();\n const filters: FolderFilters = { includeDropped: options.dropped };\n const folder = await of.getFolder(idOrName, filters);\n outputJson(folder);\n })\n );\n\n return command;\n}\n"],"mappings":"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","names":["Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command","Command"]}
|