Commit 4b6099c758fe

Vincent Demeester <vincent@sbr.pm>
2025-12-10 16:36:15
feat(org): Add children command to query direct child TODOs
- Enable querying hierarchical TODO structures by parent heading - Return only immediate children for focused task breakdown - Support org-mode task hierarchy navigation in org-manager CLI Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 408bbf2
Changed files (2)
dots
.config
claude
dots/.config/claude/skills/Org/tools/batch-functions.el
@@ -186,6 +186,32 @@ Returns list of matching headlines with context."
             (push (org-element-property :raw-value hl) sections))))
       (nreverse sections))))
 
+(defun org-batch-get-children (file heading-name)
+  "Get all direct children TODOs of HEADING-NAME in FILE.
+Returns only immediate children (level = parent + 1), not all descendants."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (org-mode)
+    (let ((children '())
+          (parent-level nil)
+          (in-subtree nil))
+      (org-element-map (org-element-parse-buffer) 'headline
+        (lambda (hl)
+          (let ((heading (org-element-property :raw-value hl))
+                (level (org-element-property :level hl)))
+            (cond
+             ;; Found the parent heading
+             ((string= heading heading-name)
+              (setq parent-level level
+                    in-subtree t))
+             ;; We're past the parent's subtree (same or lower level)
+             ((and in-subtree parent-level (<= level parent-level))
+              (setq in-subtree nil))
+             ;; We're in the subtree and this is a direct child
+             ((and in-subtree parent-level (= level (1+ parent-level)))
+              (push (org-batch--element-to-alist hl) children))))))
+      (nreverse children))))
+
 ;;; Write Operations
 
 (defun org-batch-update-state (file heading new-state)
dots/.config/claude/skills/Org/tools/org-manager
@@ -112,6 +112,9 @@ READ COMMANDS:
   sections <file>
       List all top-level sections
 
+  children <file> <heading>
+      Get direct children of a specific heading
+
 WRITE COMMANDS:
   add <file> <heading> --section=NAME [--scheduled=DATE] [--priority=N] [--tags=TAG1,TAG2]
       Add new TODO item
@@ -175,6 +178,9 @@ EXAMPLES:
   # Count by state
   org-manager count ~/desktop/org/todos.org
 
+  # Get children of a heading
+  org-manager children ~/desktop/org/todos.org "Migrate aion"
+
   # Create denote note
   org-manager denote-create "NixOS Refactoring Plan" "nixos,refactoring,plan" \\
     --category=homelab --directory=~/desktop/org/notes
@@ -330,6 +336,21 @@ cmd_sections() {
     run_elisp "$elisp"
 }
 
+cmd_children() {
+    local file="$1"
+    local heading="$2"
+
+    [[ -f "$file" ]] || error "File not found: $file"
+    [[ -n "$heading" ]] || error "Heading name required"
+
+    local elisp="(progn
+      (let ((result (org-batch-get-children \"$file\" \"$heading\")))
+        (org-batch-output-json t result))
+      (kill-emacs 0))"
+
+    run_elisp "$elisp"
+}
+
 cmd_add() {
     local file="$1"
     local heading="$2"; shift 2
@@ -641,6 +662,9 @@ main() {
         sections)
             cmd_sections "$@"
             ;;
+        children)
+            cmd_children "$@"
+            ;;
         add)
             cmd_add "$@"
             ;;