Skip to content

refactor+feat: stdin pipe, direct inbox, table output, batch ops#16

Open
crhan wants to merge 4 commits intostephendolan:mainfrom
crhan:optimize/eliminate-tempfile-and-batch-ops
Open

refactor+feat: stdin pipe, direct inbox, table output, batch ops#16
crhan wants to merge 4 commits intostephendolan:mainfrom
crhan:optimize/eliminate-tempfile-and-batch-ops

Conversation

@crhan
Copy link

@crhan crhan commented Mar 12, 2026

Summary

Commit 1: refactor — stdin pipe and direct inbox access

  • Replace temp file execution with stdin pipe: executeJXA() uses spawn() to pipe scripts via stdin instead of writing/reading/deleting temp files. Simplifies code, removes 5 unused imports.
  • Remove window dependency for inbox commands: listInboxTasks() and getInboxCount() use the inbox global object directly instead of getPerspectiveTasks('Inbox'). Inbox commands now work headlessly (no OmniFocus window needed).
  • Note: Benchmarking shows the stdin vs temp file performance difference is negligible (~650ms per call is dominated by osascript startup + Apple Events IPC). This change is about code simplification and removing the window dependency, not performance.

Commit 2: feat — human-readable table output

  • TTY (interactive terminal) → table output by default
  • Pipe / redirect → JSON output (backward compatible)
  • --json / -j flag forces JSON in terminal
  • --compact / -c unchanged (minified JSON)
  • Supported formats: task, project, tag, folder, perspective tables; key-value stats; plain text messages

Commit 3: feat — batch complete and delete

  • of task complete id1 id2 id3 — mark multiple tasks completed in a single JXA call
  • of task delete id1 id2 id3 — delete multiple tasks in a single JXA call
  • Alias: of task done, of task rm
  • Completing/deleting 10 tasks: ~650ms (one osascript call) instead of ~6.5s (ten calls)

What was investigated but not done

Custom perspective without window: Omni Automation does not expose perspective filter rules as a queryable API. The archivedFilterRules property is inaccessible, and fileWrapper is a serialization format, not a query interface. Custom perspectives still require an open window to retrieve tasks.

Test plan

  • of inbox count / of inbox list work without an open window
  • of task list, of project list, of tag list, of folder list display tables in TTY
  • of perspective list, of tag stats, of task stats display formatted output
  • of task list | jq outputs JSON (pipe detection works)
  • of --json task list forces JSON in TTY
  • of task complete "taskA" "taskB" batch-completes in one call
  • of task delete "taskA" "taskB" batch-deletes in one call
  • Single-task of task delete "taskA" still works
  • bun run test — 15/15 passing
  • bun run lint — 0 warnings, 0 errors

- Replace temp file based osascript execution with stdin pipe via spawn(),
  eliminating fs write/delete overhead on every JXA invocation.
- Rewrite listInboxTasks() to use the `inbox` global object directly
  instead of getPerspectiveTasks('Inbox'), removing the requirement for
  an open OmniFocus window.
- Optimize getInboxCount() to count tasks in-process without serializing
  every task object through JSON.
- Remove unused imports: writeFile, unlink, tmpdir, join, promisify.
@crhan crhan changed the title refactor: replace temp file with stdin pipe and optimize inbox queries refactor: use stdin pipe and direct inbox access Mar 12, 2026
crhan added 2 commits March 13, 2026 07:57
When stdout is a TTY (interactive terminal), commands now display
results as formatted tables instead of raw JSON. Pipe or redirect
output retains JSON for machine consumption. Use --json/-j to force
JSON output in a terminal.

Supported table formats:
- task list/view/search/inbox list: flag, name, project, tags, due, added
- project list/view: name, status, folder, remaining/total tasks, tags
- tag list/view: name, task count, remaining, status, parent, activity
- folder list/view: hierarchical name, status, project counts
- perspective list: name
- stats commands: key-value format
- inbox count: plain text "Count: N"

Behavior:
- TTY → table (default), --json forces JSON
- Pipe/redirect → JSON (always), backward compatible
- --compact → JSON (single line), unchanged
Add `of task complete` (alias `done`) and update `of task delete` to
accept multiple task IDs/names in a single invocation. Both execute in
a single JXA call, avoiding per-task process fork overhead.

Examples:
  of task complete id1 id2 id3
  of task done "task name A" "task name B"
  of task delete id1 id2 id3
  of task rm "task name A" "task name B"

This enables efficient bulk cleanup — completing or deleting 10 tasks
takes ~650ms (one osascript call) instead of ~6.5s (ten calls).
@crhan crhan changed the title refactor: use stdin pipe and direct inbox access refactor+feat: stdin pipe, direct inbox, table output, batch ops Mar 13, 2026
44 new tests covering:
- Type guards (isTask, isProject, isTag, isFolder, isPerspective, isRecord)
- Table utilities (pad, relativeDate, shortDate, renderTable)
- Table formatters (formatTaskTable, formatProjectTable, formatTagTable,
  formatFolderTable, formatPerspectiveTable, formatKeyValue)

Exported pure functions from output.ts to enable direct testing.

Total test count: 15 (display) + 44 (output) = 59.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant