na 1.2.86 → 1.2.88
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.
- checksums.yaml +4 -4
 - data/.cursor/commands/changelog.md +4 -0
 - data/.rubocop_todo.yml +30 -17
 - data/2025-10-29-one-more-na-update.md +142 -0
 - data/CHANGELOG.md +97 -1
 - data/Gemfile +8 -1
 - data/Gemfile.lock +40 -1
 - data/README.md +192 -2
 - data/Rakefile +78 -78
 - data/bin/commands/add.rb +31 -1
 - data/bin/commands/changes.rb +1 -0
 - data/bin/commands/complete.rb +11 -0
 - data/bin/commands/find.rb +71 -1
 - data/bin/commands/next.rb +100 -2
 - data/bin/commands/plugin.rb +75 -0
 - data/bin/commands/tagged.rb +153 -57
 - data/bin/commands/update.rb +90 -5
 - data/bin/na +7 -0
 - data/lib/na/action.rb +39 -3
 - data/lib/na/actions.rb +136 -6
 - data/lib/na/next_action.rb +180 -31
 - data/lib/na/plugins.rb +419 -0
 - data/lib/na/string.rb +15 -6
 - data/lib/na/theme.rb +1 -0
 - data/lib/na/types.rb +190 -0
 - data/lib/na/version.rb +1 -1
 - data/lib/na.rb +2 -0
 - data/na/Test.todo.markdown +32 -0
 - data/na/test.md +21 -0
 - data/na.gemspec +1 -0
 - data/plugins.md +38 -0
 - data/src/_README.md +153 -1
 - metadata +23 -1
 
    
        data/README.md
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ 
     | 
|
| 
       9 
9 
     | 
    
         
             
            _If you're one of the rare people like me who find this useful, feel free to
         
     | 
| 
       10 
10 
     | 
    
         
             
            [buy me some coffee][donate]._
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
            The current version of `na` is 1.2. 
     | 
| 
      
 12 
     | 
    
         
            +
            The current version of `na` is 1.2.88.
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            `na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
         @@ -76,7 +76,7 @@ SYNOPSIS 
     | 
|
| 
       76 
76 
     | 
    
         
             
                na [global options] command [command options] [arguments...]
         
     | 
| 
       77 
77 
     | 
    
         | 
| 
       78 
78 
     | 
    
         
             
            VERSION
         
     | 
| 
       79 
     | 
    
         
            -
                1.2. 
     | 
| 
      
 79 
     | 
    
         
            +
                1.2.88
         
     | 
| 
       80 
80 
     | 
    
         | 
| 
       81 
81 
     | 
    
         
             
            GLOBAL OPTIONS
         
     | 
| 
       82 
82 
     | 
    
         
             
                -a, --add               - Add a next action (deprecated, for backwards compatibility)
         
     | 
| 
         @@ -112,6 +112,7 @@ COMMANDS 
     | 
|
| 
       112 
112 
     | 
    
         
             
                move                - Move an existing action to a different section
         
     | 
| 
       113 
113 
     | 
    
         
             
                next, show          - Show next actions
         
     | 
| 
       114 
114 
     | 
    
         
             
                open                - Open a todo file in the default editor
         
     | 
| 
      
 115 
     | 
    
         
            +
                plugin              - Run a plugin on selected actions
         
     | 
| 
       115 
116 
     | 
    
         
             
                projects            - Show list of projects for a file
         
     | 
| 
       116 
117 
     | 
    
         
             
                prompt              - Show or install prompt hooks for the current shell
         
     | 
| 
       117 
118 
     | 
    
         
             
                restore, unfinish   - Find and remove @done tag from an action
         
     | 
| 
         @@ -152,11 +153,14 @@ DESCRIPTION 
     | 
|
| 
       152 
153 
     | 
    
         
             
            COMMAND OPTIONS
         
     | 
| 
       153 
154 
     | 
    
         
             
                --at=POSITION                   - Add task at [s]tart or [e]nd of target project (default: none)
         
     | 
| 
       154 
155 
     | 
    
         
             
                -d, --depth=DEPTH               - Search for files X directories deep (default: 1)
         
     | 
| 
      
 156 
     | 
    
         
            +
                --duration=DURATION             - Duration (e.g. 45m, 2h, 1d2h30m, or minutes) (default: none)
         
     | 
| 
      
 157 
     | 
    
         
            +
                --end, --finished=DATE          - End/Finished time (natural language or ISO) (default: none)
         
     | 
| 
       155 
158 
     | 
    
         
             
                -f, --file=PATH                 - Specify the file to which the task should be added (default: none)
         
     | 
| 
       156 
159 
     | 
    
         
             
                --finish, --done                - Mark task as @done with date
         
     | 
| 
       157 
160 
     | 
    
         
             
                --in, --todo=TODO_FILE          - Add to a known todo file, partial matches allowed (default: none)
         
     | 
| 
       158 
161 
     | 
    
         
             
                -n, --note                      - Prompt for additional notes. STDIN input (piped) will be treated as a note if present.
         
     | 
| 
       159 
162 
     | 
    
         
             
                -p, --priority=PRIO             - Add a priority level 1-5 or h, m, l (default: 0)
         
     | 
| 
      
 163 
     | 
    
         
            +
                --started=DATE                  - Started time (natural language or ISO) (default: none)
         
     | 
| 
       160 
164 
     | 
    
         
             
                -t, --tag=TAG                   - Use a tag other than the default next action tag (default: none)
         
     | 
| 
       161 
165 
     | 
    
         
             
                --to, --project, --proj=PROJECT - Add action to specific project (default: Inbox)
         
     | 
| 
       162 
166 
     | 
    
         
             
                -x                              - Don't add next action tag to new entry
         
     | 
| 
         @@ -221,18 +225,24 @@ DESCRIPTION 
     | 
|
| 
       221 
225 
     | 
    
         | 
| 
       222 
226 
     | 
    
         
             
            COMMAND OPTIONS
         
     | 
| 
       223 
227 
     | 
    
         
             
                -d, --depth=DEPTH                      - Recurse to depth (default: none)
         
     | 
| 
      
 228 
     | 
    
         
            +
                --divider=STRING                       - Divider string for text IO (default: none)
         
     | 
| 
       224 
229 
     | 
    
         
             
                --[no-]done                            - Include @done actions
         
     | 
| 
       225 
230 
     | 
    
         
             
                -e, --regex                            - Interpret search pattern as regular expression
         
     | 
| 
      
 231 
     | 
    
         
            +
                --human                                - Format durations in human-friendly form
         
     | 
| 
       226 
232 
     | 
    
         
             
                --in=TODO_PATH                         - Show actions from a specific todo file in history. May use wildcards (* and ?) (default: none)
         
     | 
| 
      
 233 
     | 
    
         
            +
                --input=TYPE                           - Plugin input format (json|yaml|csv|text) (default: none)
         
     | 
| 
       227 
234 
     | 
    
         
             
                --nest                                 - Output actions nested by file
         
     | 
| 
       228 
235 
     | 
    
         
             
                --no_file                              - No filename in output
         
     | 
| 
       229 
236 
     | 
    
         
             
                --[no-]notes                           - Include notes in output
         
     | 
| 
       230 
237 
     | 
    
         
             
                -o, --or                               - Combine search tokens with OR, displaying actions matching ANY of the terms
         
     | 
| 
       231 
238 
     | 
    
         
             
                --omnifocus                            - Output actions nested by file and project
         
     | 
| 
      
 239 
     | 
    
         
            +
                --output=TYPE                          - Plugin output format (json|yaml|csv|text) (default: none)
         
     | 
| 
      
 240 
     | 
    
         
            +
                --plugin=NAME                          - Run a plugin on results (STDOUT only; no file writes) (default: none)
         
     | 
| 
       232 
241 
     | 
    
         
             
                --proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
         
     | 
| 
       233 
242 
     | 
    
         
             
                --save=TITLE                           - Save this search for future use (default: none)
         
     | 
| 
       234 
243 
     | 
    
         
             
                --[no-]search_notes                    - Include notes in search (default: enabled)
         
     | 
| 
       235 
244 
     | 
    
         
             
                --tagged=TAG                           - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
         
     | 
| 
      
 245 
     | 
    
         
            +
                --times                                - Show per-action durations and total
         
     | 
| 
       236 
246 
     | 
    
         
             
                -v, --invert                           - Show actions not matching search pattern
         
     | 
| 
       237 
247 
     | 
    
         
             
                -x, --exact                            - Match pattern exactly
         
     | 
| 
       238 
248 
     | 
    
         | 
| 
         @@ -333,16 +343,24 @@ DESCRIPTION 
     | 
|
| 
       333 
343 
     | 
    
         
             
            COMMAND OPTIONS
         
     | 
| 
       334 
344 
     | 
    
         
             
                --all                                  - Show next actions from all known todo files (in any directory)
         
     | 
| 
       335 
345 
     | 
    
         
             
                -d, --depth=DEPTH                      - Recurse to depth (default: none)
         
     | 
| 
      
 346 
     | 
    
         
            +
                --divider=STRING                       - Divider string for text IO (default: none)
         
     | 
| 
       336 
347 
     | 
    
         
             
                --[no-]done                            - Include @done actions
         
     | 
| 
       337 
348 
     | 
    
         
             
                --exact                                - Search query is exact text match (not tokens)
         
     | 
| 
       338 
349 
     | 
    
         
             
                --file=TODO_FILE                       - Display matches from specific todo file ([relative] path) (default: none)
         
     | 
| 
       339 
350 
     | 
    
         
             
                --hidden                               - Include hidden directories while traversing
         
     | 
| 
      
 351 
     | 
    
         
            +
                --human                                - Format durations in human-friendly form
         
     | 
| 
       340 
352 
     | 
    
         
             
                --in, --todo=TODO                      - Display matches from a known todo file anywhere in history (short name) (may be used more than once, default: none)
         
     | 
| 
      
 353 
     | 
    
         
            +
                --input=TYPE                           - Plugin input format (json|yaml|csv|text) (default: none)
         
     | 
| 
      
 354 
     | 
    
         
            +
                --json_times                           - Output times as JSON object (implies --times and --done)
         
     | 
| 
       341 
355 
     | 
    
         
             
                --nest                                 - Output actions nested by file
         
     | 
| 
       342 
356 
     | 
    
         
             
                --no_file                              - No filename in output
         
     | 
| 
       343 
357 
     | 
    
         
             
                --[no-]notes                           - Include notes in output
         
     | 
| 
       344 
358 
     | 
    
         
             
                --omnifocus                            - Output actions nested by file and project
         
     | 
| 
      
 359 
     | 
    
         
            +
                --only_timed                           - Show only actions that have a duration (@started and @done)
         
     | 
| 
      
 360 
     | 
    
         
            +
                --only_times                           - Output only elapsed time totals (implies --times and --done)
         
     | 
| 
      
 361 
     | 
    
         
            +
                --output=TYPE                          - Plugin output format (json|yaml|csv|text) (default: none)
         
     | 
| 
       345 
362 
     | 
    
         
             
                -p, --prio, --priority=PRIORITY        - Match actions with priority, allows <>= comparison (may be used more than once, default: none)
         
     | 
| 
      
 363 
     | 
    
         
            +
                --plugin=NAME                          - Run a plugin on results (STDOUT only; no file writes) (default: none)
         
     | 
| 
       346 
364 
     | 
    
         
             
                --proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
         
     | 
| 
       347 
365 
     | 
    
         
             
                --regex                                - Search query is regular expression
         
     | 
| 
       348 
366 
     | 
    
         
             
                --save=TITLE                           - Save this search for future use (default: none)
         
     | 
| 
         @@ -350,6 +368,7 @@ COMMAND OPTIONS 
     | 
|
| 
       350 
368 
     | 
    
         
             
                --[no-]search_notes                    - Include notes in search (default: enabled)
         
     | 
| 
       351 
369 
     | 
    
         
             
                -t, --tag=TAG                          - Alternate tag to search for (default: none)
         
     | 
| 
       352 
370 
     | 
    
         
             
                --tagged=TAG                           - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
         
     | 
| 
      
 371 
     | 
    
         
            +
                --times                                - Show per-action durations and total
         
     | 
| 
       353 
372 
     | 
    
         | 
| 
       354 
373 
     | 
    
         
             
            EXAMPLES
         
     | 
| 
       355 
374 
     | 
    
         | 
| 
         @@ -489,19 +508,28 @@ DESCRIPTION 
     | 
|
| 
       489 
508 
     | 
    
         | 
| 
       490 
509 
     | 
    
         
             
            COMMAND OPTIONS
         
     | 
| 
       491 
510 
     | 
    
         
             
                -d, --depth=DEPTH                      - Recurse to depth (default: 1)
         
     | 
| 
      
 511 
     | 
    
         
            +
                --divider=STRING                       - Divider string for text IO (default: none)
         
     | 
| 
       492 
512 
     | 
    
         
             
                --[no-]done                            - Include @done actions
         
     | 
| 
       493 
513 
     | 
    
         
             
                --exact                                - Search query is exact text match (not tokens)
         
     | 
| 
      
 514 
     | 
    
         
            +
                --human                                - Format durations in human-friendly form
         
     | 
| 
       494 
515 
     | 
    
         
             
                --in=TODO_PATH                         - Show actions from a specific todo file in history. May use wildcards (* and ?) (default: none)
         
     | 
| 
      
 516 
     | 
    
         
            +
                --input=TYPE                           - Plugin input format (json|yaml|csv|text) (default: none)
         
     | 
| 
      
 517 
     | 
    
         
            +
                --json_times                           - Output times as JSON object (implies --times and --done)
         
     | 
| 
       495 
518 
     | 
    
         
             
                --nest                                 - Output actions nested by file
         
     | 
| 
       496 
519 
     | 
    
         
             
                --no_file                              - No filename in output
         
     | 
| 
       497 
520 
     | 
    
         
             
                --[no-]notes                           - Include notes in output
         
     | 
| 
       498 
521 
     | 
    
         
             
                -o, --or                               - Combine tags with OR, displaying actions matching ANY of the tags
         
     | 
| 
       499 
522 
     | 
    
         
             
                --omnifocus                            - Output actions nested by file and project
         
     | 
| 
      
 523 
     | 
    
         
            +
                --only_timed                           - Show only actions that have a duration (@started and @done)
         
     | 
| 
      
 524 
     | 
    
         
            +
                --only_times                           - Output only elapsed time totals (implies --times and --done)
         
     | 
| 
      
 525 
     | 
    
         
            +
                --output=TYPE                          - Plugin output format (json|yaml|csv|text) (default: none)
         
     | 
| 
      
 526 
     | 
    
         
            +
                --plugin=NAME                          - Run a plugin on results (STDOUT only; no file writes) (default: none)
         
     | 
| 
       500 
527 
     | 
    
         
             
                --proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
         
     | 
| 
       501 
528 
     | 
    
         
             
                --regex                                - Search query is regular expression
         
     | 
| 
       502 
529 
     | 
    
         
             
                --save=TITLE                           - Save this search for future use (default: none)
         
     | 
| 
       503 
530 
     | 
    
         
             
                --search, --find, --grep=QUERY         - Filter results using search terms (may be used more than once, default: none)
         
     | 
| 
       504 
531 
     | 
    
         
             
                --[no-]search_notes                    - Include notes in search (default: enabled)
         
     | 
| 
      
 532 
     | 
    
         
            +
                --times                                - Show per-action durations and total
         
     | 
| 
       505 
533 
     | 
    
         
             
                -v, --invert                           - Show actions not matching tags
         
     | 
| 
       506 
534 
     | 
    
         | 
| 
       507 
535 
     | 
    
         
             
            EXAMPLES
         
     | 
| 
         @@ -594,21 +622,28 @@ COMMAND OPTIONS 
     | 
|
| 
       594 
622 
     | 
    
         
             
                --at=POSITION                          - When moving task, add at [s]tart or [e]nd of target project (default: none)
         
     | 
| 
       595 
623 
     | 
    
         
             
                -d, --depth=DEPTH                      - Search for files X directories deep (default: 1)
         
     | 
| 
       596 
624 
     | 
    
         
             
                --delete                               - Delete an action
         
     | 
| 
      
 625 
     | 
    
         
            +
                --divider=STRING                       - Divider string for text IO (default: none)
         
     | 
| 
       597 
626 
     | 
    
         
             
                --[no-]done                            - Include @done actions
         
     | 
| 
      
 627 
     | 
    
         
            +
                --duration=DURATION                    - Duration (e.g. 45m, 2h, 1d2h30m, or minutes) (default: none)
         
     | 
| 
       598 
628 
     | 
    
         
             
                -e, --regex                            - Interpret search pattern as regular expression
         
     | 
| 
       599 
629 
     | 
    
         
             
                --edit                                 - Open action in editor (vim).             Natural language dates will be parsed and converted in date-based tags.
         
     | 
| 
      
 630 
     | 
    
         
            +
                --end, --finished=DATE                 - End/Finished time (natural language or ISO) (default: none)
         
     | 
| 
       600 
631 
     | 
    
         
             
                -f, --finish                           - Add a @done tag to action
         
     | 
| 
       601 
632 
     | 
    
         
             
                --file=PATH                            - Specify the file to search for the task (default: none)
         
     | 
| 
       602 
633 
     | 
    
         
             
                --in, --todo=TODO_FILE                 - Use a known todo file, partial matches allowed (default: none)
         
     | 
| 
      
 634 
     | 
    
         
            +
                --input=TYPE                           - Plugin input format (json|yaml|csv|text) (default: none)
         
     | 
| 
       603 
635 
     | 
    
         
             
                -n, --note                             - Prompt for additional notes. Input will be appended to any existing note.     If STDIN input (piped) is detected, it will be used as a note.
         
     | 
| 
       604 
636 
     | 
    
         
             
                -o, --overwrite                        - Overwrite note instead of appending
         
     | 
| 
      
 637 
     | 
    
         
            +
                --output=TYPE                          - Plugin output format (json|yaml|csv|text) (default: none)
         
     | 
| 
       605 
638 
     | 
    
         
             
                -p, --priority=PRIO                    - Add/change a priority level 1-5 (default: 0)
         
     | 
| 
      
 639 
     | 
    
         
            +
                --plugin=NAME                          - Run a plugin by name on selected actions (default: none)
         
     | 
| 
       606 
640 
     | 
    
         
             
                --proj, --project=PROJECT[/SUBPROJECT] - Affect actions from a specific project (default: none)
         
     | 
| 
       607 
641 
     | 
    
         
             
                -r, --remove=TAG                       - Remove a tag from the action, use multiple times or combine multiple tags with a comma,             wildcards (* and ?) allowed (may be used more than once, default: none)
         
     | 
| 
       608 
642 
     | 
    
         
             
                --replace=TEXT                         - Use with --find to find and replace with new text. Enables --exact when used (default: none)
         
     | 
| 
       609 
643 
     | 
    
         
             
                --restore                              - Remove @done tag from action
         
     | 
| 
       610 
644 
     | 
    
         
             
                --search, --find, --grep=QUERY         - Filter results using search terms (may be used more than once, default: none)
         
     | 
| 
       611 
645 
     | 
    
         
             
                --[no-]search_notes                    - Include notes in search (default: enabled)
         
     | 
| 
      
 646 
     | 
    
         
            +
                --started=DATE                         - Started time (natural language or ISO) (default: none)
         
     | 
| 
       612 
647 
     | 
    
         
             
                -t, --tag=TAG                          - Add a tag to the action, @tag(values) allowed, use multiple times or combine multiple tags with a comma (may be used more than once, default: none)
         
     | 
| 
       613 
648 
     | 
    
         
             
                --tagged=TAG                           - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
         
     | 
| 
       614 
649 
     | 
    
         
             
                --to, --move=PROJECT                   - Move action to specific project (default: none)
         
     | 
| 
         @@ -626,6 +661,49 @@ EXAMPLES 
     | 
|
| 
       626 
661 
     | 
    
         
             
                na update --archive My cool action
         
     | 
| 
       627 
662 
     | 
    
         
             
            ```
         
     | 
| 
       628 
663 
     | 
    
         | 
| 
      
 664 
     | 
    
         
            +
            #### Time tracking
         
     | 
| 
      
 665 
     | 
    
         
            +
             
     | 
| 
      
 666 
     | 
    
         
            +
            `na` supports tracking elapsed time between a start and finish for actions using `@started(YYYY-MM-DD HH:MM)` and `@done(YYYY-MM-DD HH:MM)` tags. Durations are not stored; they are calculated on the fly from these tags.
         
     | 
| 
      
 667 
     | 
    
         
            +
             
     | 
| 
      
 668 
     | 
    
         
            +
            - Add/Finish/Update flags:
         
     | 
| 
      
 669 
     | 
    
         
            +
              - `--started TIME` set a start time when creating or finishing an item
         
     | 
| 
      
 670 
     | 
    
         
            +
              - `--end TIME` (alias `--finished`) set a done time
         
     | 
| 
      
 671 
     | 
    
         
            +
              - `--duration DURATION` backfill start time from the provided end time
         
     | 
| 
      
 672 
     | 
    
         
            +
              - All flags accept natural language (via Chronic) and shorthand: `30m ago`, `-2h`, `2h30m`, `2:30 ago`, `yesterday 5pm`
         
     | 
| 
      
 673 
     | 
    
         
            +
             
     | 
| 
      
 674 
     | 
    
         
            +
            Examples:
         
     | 
| 
      
 675 
     | 
    
         
            +
             
     | 
| 
      
 676 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 677 
     | 
    
         
            +
            na add --started "30 minutes ago" "Investigate bug"
         
     | 
| 
      
 678 
     | 
    
         
            +
            na complete --finished now --duration 2h30m "Investigate bug"
         
     | 
| 
      
 679 
     | 
    
         
            +
            na update --started "yesterday 3pm" --end "yesterday 5:15pm" "Investigate bug"
         
     | 
| 
      
 680 
     | 
    
         
            +
            ```
         
     | 
| 
      
 681 
     | 
    
         
            +
             
     | 
| 
      
 682 
     | 
    
         
            +
            - Display flags (next/tagged):
         
     | 
| 
      
 683 
     | 
    
         
            +
              - `--times` show per???action durations and a grand total (implies `--done`)
         
     | 
| 
      
 684 
     | 
    
         
            +
              - `--human` format durations as human???readable text instead of `DD:HH:MM:SS`
         
     | 
| 
      
 685 
     | 
    
         
            +
              - `--only_timed` show only actions that have both `@started` and `@done` (implies `--times --done`)
         
     | 
| 
      
 686 
     | 
    
         
            +
              - `--only_times` output only the totals section (no action lines; implies `--times --done`)
         
     | 
| 
      
 687 
     | 
    
         
            +
              - `--json_times` output a JSON object with timed items, per???tag totals, and overall total (implies `--times --done`)
         
     | 
| 
      
 688 
     | 
    
         
            +
             
     | 
| 
      
 689 
     | 
    
         
            +
            Example outputs:
         
     | 
| 
      
 690 
     | 
    
         
            +
             
     | 
| 
      
 691 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 692 
     | 
    
         
            +
            # Per???action durations appended and totals table
         
     | 
| 
      
 693 
     | 
    
         
            +
            na next --times --human
         
     | 
| 
      
 694 
     | 
    
         
            +
             
     | 
| 
      
 695 
     | 
    
         
            +
            # Only totals table (Markdown), no action lines
         
     | 
| 
      
 696 
     | 
    
         
            +
            na tagged "tag*=bug" --only_times
         
     | 
| 
      
 697 
     | 
    
         
            +
             
     | 
| 
      
 698 
     | 
    
         
            +
            # JSON for scripting
         
     | 
| 
      
 699 
     | 
    
         
            +
            na next --json_times > times.json
         
     | 
| 
      
 700 
     | 
    
         
            +
            ```
         
     | 
| 
      
 701 
     | 
    
         
            +
             
     | 
| 
      
 702 
     | 
    
         
            +
            Notes:
         
     | 
| 
      
 703 
     | 
    
         
            +
             
     | 
| 
      
 704 
     | 
    
         
            +
            - Any newly added or edited action text is scanned for natural???language values in `@started(...)`/`@done(...)` and normalized to `YYYY???MM???DD HH:MM`.
         
     | 
| 
      
 705 
     | 
    
         
            +
            - The color of durations in output is configurable via the theme key `duration` (defaults to `{y}`).
         
     | 
| 
      
 706 
     | 
    
         
            +
             
     | 
| 
       629 
707 
     | 
    
         
             
            ##### changelog
         
     | 
| 
       630 
708 
     | 
    
         | 
| 
       631 
709 
     | 
    
         
             
            View recent changes with `na changelog` or `na changes`.
         
     | 
| 
         @@ -655,7 +733,9 @@ COMMAND OPTIONS 
     | 
|
| 
       655 
733 
     | 
    
         
             
                -a, --archive                          - Add a @done tag to action and move to Archive
         
     | 
| 
       656 
734 
     | 
    
         
             
                --all                                  - Act on all matches immediately (no menu)
         
     | 
| 
       657 
735 
     | 
    
         
             
                -d, --depth=DEPTH                      - Search for files X directories deep (default: 1)
         
     | 
| 
      
 736 
     | 
    
         
            +
                --duration=DURATION                    - Duration (e.g. 45m, 2h, 1d2h30m, or minutes) (default: none)
         
     | 
| 
       658 
737 
     | 
    
         
             
                -e, --regex                            - Interpret search pattern as regular expression
         
     | 
| 
      
 738 
     | 
    
         
            +
                --end, --finished=DATE                 - End/Finished time (natural language or ISO) (default: none)
         
     | 
| 
       659 
739 
     | 
    
         
             
                --file=PATH                            - Specify the file to search for the task (default: none)
         
     | 
| 
       660 
740 
     | 
    
         
             
                --in, --todo=TODO_FILE                 - Use a known todo file, partial matches allowed (default: none)
         
     | 
| 
       661 
741 
     | 
    
         
             
                -n, --note                             - Prompt for additional notes. Input will be appended to any existing note.     If STDIN input (piped) is detected, it will be used as a note.
         
     | 
| 
         @@ -663,6 +743,7 @@ COMMAND OPTIONS 
     | 
|
| 
       663 
743 
     | 
    
         
             
                --proj, --project=PROJECT[/SUBPROJECT] - Affect actions from a specific project (default: none)
         
     | 
| 
       664 
744 
     | 
    
         
             
                --search, --find, --grep=QUERY         - Filter results using search terms (may be used more than once, default: none)
         
     | 
| 
       665 
745 
     | 
    
         
             
                --[no-]search_notes                    - Include notes in search (default: enabled)
         
     | 
| 
      
 746 
     | 
    
         
            +
                --started=DATE                         - Started time (natural language or ISO) (default: none)
         
     | 
| 
       666 
747 
     | 
    
         
             
                --tagged=TAG                           - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
         
     | 
| 
       667 
748 
     | 
    
         
             
                --to, --move=PROJECT                   - Add a @done tag and move action to specific project (default: none)
         
     | 
| 
       668 
749 
     | 
    
         
             
                -x, --exact                            - Match pattern exactly
         
     | 
| 
         @@ -834,6 +915,115 @@ If you're using a single global file, you'll need `--cwd_as` to be `tag` or `pro 
     | 
|
| 
       834 
915 
     | 
    
         | 
| 
       835 
916 
     | 
    
         
             
            After installing a hook, you'll need to close your terminal and start a new session to initialize the new commands.
         
     | 
| 
       836 
917 
     | 
    
         | 
| 
      
 918 
     | 
    
         
            +
            ### Plugins
         
     | 
| 
      
 919 
     | 
    
         
            +
             
     | 
| 
      
 920 
     | 
    
         
            +
            NA supports a plugin system that allows you to run external scripts to transform or process actions. Plugins are stored in `~/.local/share/na/plugins` and can be written in any language with a shebang.
         
     | 
| 
      
 921 
     | 
    
         
            +
             
     | 
| 
      
 922 
     | 
    
         
            +
            #### Getting Started
         
     | 
| 
      
 923 
     | 
    
         
            +
             
     | 
| 
      
 924 
     | 
    
         
            +
            The first time NA runs, it will create the plugins directory with a README and two sample plugins:
         
     | 
| 
      
 925 
     | 
    
         
            +
            - `Add Foo.py` - Adds a `@foo` tag with a timestamp
         
     | 
| 
      
 926 
     | 
    
         
            +
            - `Add Bar.sh` - Adds a `@bar` tag
         
     | 
| 
      
 927 
     | 
    
         
            +
             
     | 
| 
      
 928 
     | 
    
         
            +
            You can delete or modify these sample plugins as needed.
         
     | 
| 
      
 929 
     | 
    
         
            +
             
     | 
| 
      
 930 
     | 
    
         
            +
            #### Running Plugins
         
     | 
| 
      
 931 
     | 
    
         
            +
             
     | 
| 
      
 932 
     | 
    
         
            +
            Run a plugin with:
         
     | 
| 
      
 933 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 934 
     | 
    
         
            +
            na plugin PLUGIN_NAME
         
     | 
| 
      
 935 
     | 
    
         
            +
            ```
         
     | 
| 
      
 936 
     | 
    
         
            +
             
     | 
| 
      
 937 
     | 
    
         
            +
            Or use plugins through the `update` command's interactive menu, or pipe actions through plugins on display commands:
         
     | 
| 
      
 938 
     | 
    
         
            +
             
     | 
| 
      
 939 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 940 
     | 
    
         
            +
            na update --plugin PLUGIN_NAME           # Run plugin on selected actions
         
     | 
| 
      
 941 
     | 
    
         
            +
            na next --plugin PLUGIN_NAME             # Transform output only (no file writes)
         
     | 
| 
      
 942 
     | 
    
         
            +
            na tagged bug --plugin PLUGIN_NAME       # Filter and transform
         
     | 
| 
      
 943 
     | 
    
         
            +
            na find "search term" --plugin PLUGIN_NAME
         
     | 
| 
      
 944 
     | 
    
         
            +
            ```
         
     | 
| 
      
 945 
     | 
    
         
            +
             
     | 
| 
      
 946 
     | 
    
         
            +
            #### Plugin Metadata
         
     | 
| 
      
 947 
     | 
    
         
            +
             
     | 
| 
      
 948 
     | 
    
         
            +
            Plugins can specify their behavior in a metadata block after the shebang:
         
     | 
| 
      
 949 
     | 
    
         
            +
             
     | 
| 
      
 950 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 951 
     | 
    
         
            +
            #!/usr/bin/env python3
         
     | 
| 
      
 952 
     | 
    
         
            +
            # name: My Plugin
         
     | 
| 
      
 953 
     | 
    
         
            +
            # input: json
         
     | 
| 
      
 954 
     | 
    
         
            +
            # output: json
         
     | 
| 
      
 955 
     | 
    
         
            +
            ```
         
     | 
| 
      
 956 
     | 
    
         
            +
             
     | 
| 
      
 957 
     | 
    
         
            +
            Available metadata keys (case-insensitive):
         
     | 
| 
      
 958 
     | 
    
         
            +
            - `input`: Input format (`json`, `yaml`, `csv`, `text`)
         
     | 
| 
      
 959 
     | 
    
         
            +
            - `output`: Output format
         
     | 
| 
      
 960 
     | 
    
         
            +
            - `name` or `title`: Display name (defaults to filename)
         
     | 
| 
      
 961 
     | 
    
         
            +
             
     | 
| 
      
 962 
     | 
    
         
            +
            #### Input/Output Formats
         
     | 
| 
      
 963 
     | 
    
         
            +
             
     | 
| 
      
 964 
     | 
    
         
            +
            Plugins accept and return action data. Use `--input` and `--output` flags to override metadata:
         
     | 
| 
      
 965 
     | 
    
         
            +
             
     | 
| 
      
 966 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 967 
     | 
    
         
            +
            na plugin MY_PLUGIN --input text --output json --divider "||"
         
     | 
| 
      
 968 
     | 
    
         
            +
            ```
         
     | 
| 
      
 969 
     | 
    
         
            +
             
     | 
| 
      
 970 
     | 
    
         
            +
            **JSON/YAML Schema:**
         
     | 
| 
      
 971 
     | 
    
         
            +
            ```json
         
     | 
| 
      
 972 
     | 
    
         
            +
            [
         
     | 
| 
      
 973 
     | 
    
         
            +
              {
         
     | 
| 
      
 974 
     | 
    
         
            +
                "file_path": "todo.taskpaper",
         
     | 
| 
      
 975 
     | 
    
         
            +
                "line": 15,
         
     | 
| 
      
 976 
     | 
    
         
            +
                "parents": ["Project", "Subproject"],
         
     | 
| 
      
 977 
     | 
    
         
            +
                "text": "- Action text @tag(value)",
         
     | 
| 
      
 978 
     | 
    
         
            +
                "note": "Note content",
         
     | 
| 
      
 979 
     | 
    
         
            +
                "tags": [
         
     | 
| 
      
 980 
     | 
    
         
            +
                  { "name": "tag", "value": "value" }
         
     | 
| 
      
 981 
     | 
    
         
            +
                ]
         
     | 
| 
      
 982 
     | 
    
         
            +
              }
         
     | 
| 
      
 983 
     | 
    
         
            +
            ]
         
     | 
| 
      
 984 
     | 
    
         
            +
            ```
         
     | 
| 
      
 985 
     | 
    
         
            +
             
     | 
| 
      
 986 
     | 
    
         
            +
            **Text Format:**
         
     | 
| 
      
 987 
     | 
    
         
            +
            ```
         
     | 
| 
      
 988 
     | 
    
         
            +
            ACTION||ARGS||file_path:line||parents||text||note||tags
         
     | 
| 
      
 989 
     | 
    
         
            +
            ```
         
     | 
| 
      
 990 
     | 
    
         
            +
             
     | 
| 
      
 991 
     | 
    
         
            +
            Default divider is `||` (configurable with `--divider`).
         
     | 
| 
      
 992 
     | 
    
         
            +
            - `parents`: `Parent>Child>Leaf`
         
     | 
| 
      
 993 
     | 
    
         
            +
            - `tags`: `name(value);name;other(value)`
         
     | 
| 
      
 994 
     | 
    
         
            +
             
     | 
| 
      
 995 
     | 
    
         
            +
            If the first token isn???t a known action, it???s treated as `file_path:line` and the action defaults to UPDATE.
         
     | 
| 
      
 996 
     | 
    
         
            +
             
     | 
| 
      
 997 
     | 
    
         
            +
            #### Actions
         
     | 
| 
      
 998 
     | 
    
         
            +
             
     | 
| 
      
 999 
     | 
    
         
            +
            Plugins may return an optional ACTION with arguments. Supported (case-insensitive):
         
     | 
| 
      
 1000 
     | 
    
         
            +
            - UPDATE (default; replace text/note/tags/parents)
         
     | 
| 
      
 1001 
     | 
    
         
            +
            - DELETE
         
     | 
| 
      
 1002 
     | 
    
         
            +
            - COMPLETE/FINISH
         
     | 
| 
      
 1003 
     | 
    
         
            +
            - RESTORE/UNFINISH
         
     | 
| 
      
 1004 
     | 
    
         
            +
            - ARCHIVE
         
     | 
| 
      
 1005 
     | 
    
         
            +
            - ADD_TAG (args: one or more tags)
         
     | 
| 
      
 1006 
     | 
    
         
            +
            - DELETE_TAG/REMOVE_TAG (args: one or more tags)
         
     | 
| 
      
 1007 
     | 
    
         
            +
            - MOVE (args: target project path)
         
     | 
| 
      
 1008 
     | 
    
         
            +
             
     | 
| 
      
 1009 
     | 
    
         
            +
            #### Plugin Behavior
         
     | 
| 
      
 1010 
     | 
    
         
            +
             
     | 
| 
      
 1011 
     | 
    
         
            +
            **On `update` or `plugin` command:**
         
     | 
| 
      
 1012 
     | 
    
         
            +
            - Plugins can modify text, notes, tags, and parents
         
     | 
| 
      
 1013 
     | 
    
         
            +
            - Changing `parents` will move the action to the new project location
         
     | 
| 
      
 1014 
     | 
    
         
            +
            - `file_path` and `line` cannot be changed
         
     | 
| 
      
 1015 
     | 
    
         
            +
             
     | 
| 
      
 1016 
     | 
    
         
            +
            **On display commands (`next`, `tagged`, `find`):**
         
     | 
| 
      
 1017 
     | 
    
         
            +
            - Plugins only transform STDOUT (no file writes)
         
     | 
| 
      
 1018 
     | 
    
         
            +
            - Use returned text/note/tags/parents for rendering
         
     | 
| 
      
 1019 
     | 
    
         
            +
            - Parent changes affect display but not file structure
         
     | 
| 
      
 1020 
     | 
    
         
            +
             
     | 
| 
      
 1021 
     | 
    
         
            +
            #### Override Formats
         
     | 
| 
      
 1022 
     | 
    
         
            +
             
     | 
| 
      
 1023 
     | 
    
         
            +
            You can override plugin defaults with flags on any command that supports `--plugin`:
         
     | 
| 
      
 1024 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 1025 
     | 
    
         
            +
            na next --plugin FOO --input csv --output text
         
     | 
| 
      
 1026 
     | 
    
         
            +
            ```
         
     | 
| 
       837 
1027 
     | 
    
         | 
| 
       838 
1028 
     | 
    
         
             
            [fzf]: https://github.com/junegunn/fzf
         
     | 
| 
       839 
1029 
     | 
    
         
             
            [gum]: https://github.com/charmbracelet/gum
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,70 +1,70 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require  
     | 
| 
       2 
     | 
    
         
            -
            require  
     | 
| 
       3 
     | 
    
         
            -
            require  
     | 
| 
       4 
     | 
    
         
            -
            require  
     | 
| 
       5 
     | 
    
         
            -
            require  
     | 
| 
       6 
     | 
    
         
            -
            require  
     | 
| 
       7 
     | 
    
         
            -
            require  
     | 
| 
       8 
     | 
    
         
            -
            require  
     | 
| 
       9 
     | 
    
         
            -
            require  
     | 
| 
       10 
     | 
    
         
            -
            require  
     | 
| 
       11 
     | 
    
         
            -
            require  
     | 
| 
      
 1 
     | 
    
         
            +
            require 'rake/clean'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rubygems/package_task'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'rdoc/task'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'bump/tasks'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'bundler/gem_tasks'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'rspec/core/rake_task'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'rubocop/rake_task'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'yard'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'tty-spinner'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'English'
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
            YARD::Rake::YardocTask.new do |t|
         
     | 
| 
       14 
     | 
    
         
            -
              t.files = [ 
     | 
| 
       15 
     | 
    
         
            -
              t.options = [ 
     | 
| 
       16 
     | 
    
         
            -
              t.stats_options = [ 
     | 
| 
      
 14 
     | 
    
         
            +
              t.files = ['lib/na/*.rb']
         
     | 
| 
      
 15 
     | 
    
         
            +
              t.options = ['--markup-provider=redcarpet', '--markup=markdown', '--no-private', '-p', 'yard_templates']
         
     | 
| 
      
 16 
     | 
    
         
            +
              t.stats_options = ['--list-undoc'] # Uncommented this line for stats options
         
     | 
| 
       17 
17 
     | 
    
         
             
            end
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            ## Docker error class
         
     | 
| 
       20 
20 
     | 
    
         
             
            class DockerError < StandardError
         
     | 
| 
       21 
21 
     | 
    
         
             
              def initialize(msg = nil)
         
     | 
| 
       22 
     | 
    
         
            -
                msg = msg ? "Docker error: #{msg}" :  
     | 
| 
       23 
     | 
    
         
            -
                super 
     | 
| 
      
 22 
     | 
    
         
            +
                msg = msg ? "Docker error: #{msg}" : 'Docker error'
         
     | 
| 
      
 23 
     | 
    
         
            +
                super
         
     | 
| 
       24 
24 
     | 
    
         
             
              end
         
     | 
| 
       25 
25 
     | 
    
         
             
            end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
            task default: %i[test yard]
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
            desc  
     | 
| 
      
 29 
     | 
    
         
            +
            desc 'Run test suite'
         
     | 
| 
       30 
30 
     | 
    
         
             
            task test: %i[rubocop spec]
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
32 
     | 
    
         
             
            RSpec::Core::RakeTask.new do |t|
         
     | 
| 
       33 
     | 
    
         
            -
              t.rspec_opts =  
     | 
| 
      
 33 
     | 
    
         
            +
              t.rspec_opts = '--format documentation'
         
     | 
| 
       34 
34 
     | 
    
         
             
            end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
            RuboCop::RakeTask.new do |t|
         
     | 
| 
       37 
     | 
    
         
            -
              t.formatters = [ 
     | 
| 
      
 37 
     | 
    
         
            +
              t.formatters = ['progress']
         
     | 
| 
       38 
38 
     | 
    
         
             
            end
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
            task :doc, [*Rake.application[:yard].arg_names] => [:yard]
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
            Rake::RDocTask.new do |rd|
         
     | 
| 
       43 
     | 
    
         
            -
              rd.main =  
     | 
| 
       44 
     | 
    
         
            -
              rd.rdoc_files.include( 
     | 
| 
       45 
     | 
    
         
            -
              rd.title =  
     | 
| 
      
 43 
     | 
    
         
            +
              rd.main = 'README.rdoc'
         
     | 
| 
      
 44 
     | 
    
         
            +
              rd.rdoc_files.include('README.rdoc', 'lib/**/*.rb', 'bin/**/*')
         
     | 
| 
      
 45 
     | 
    
         
            +
              rd.title = 'na'
         
     | 
| 
       46 
46 
     | 
    
         
             
            end
         
     | 
| 
       47 
47 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
            spec = eval(File.read( 
     | 
| 
      
 48 
     | 
    
         
            +
            spec = eval(File.read('na.gemspec'))
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
            Gem::PackageTask.new(spec) do |pkg|
         
     | 
| 
       51 
51 
     | 
    
         
             
            end
         
     | 
| 
       52 
     | 
    
         
            -
            require  
     | 
| 
      
 52 
     | 
    
         
            +
            require 'rake/testtask'
         
     | 
| 
       53 
53 
     | 
    
         
             
            Rake::TestTask.new do |t|
         
     | 
| 
       54 
     | 
    
         
            -
              t.libs <<  
     | 
| 
       55 
     | 
    
         
            -
              t.test_files = FileList[ 
     | 
| 
      
 54 
     | 
    
         
            +
              t.libs << 'test'
         
     | 
| 
      
 55 
     | 
    
         
            +
              t.test_files = FileList['test/*_test.rb']
         
     | 
| 
       56 
56 
     | 
    
         
             
            end
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
            desc  
     | 
| 
      
 58 
     | 
    
         
            +
            desc 'Install current gem in all versions of asdf-controlled ruby'
         
     | 
| 
       59 
59 
     | 
    
         
             
            task :install do
         
     | 
| 
       60 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       61 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       62 
     | 
    
         
            -
              Dir.chdir  
     | 
| 
       63 
     | 
    
         
            -
              file = Dir.glob( 
     | 
| 
      
 60 
     | 
    
         
            +
              Rake::Task['clobber'].invoke
         
     | 
| 
      
 61 
     | 
    
         
            +
              Rake::Task['package'].invoke
         
     | 
| 
      
 62 
     | 
    
         
            +
              Dir.chdir 'pkg'
         
     | 
| 
      
 63 
     | 
    
         
            +
              file = Dir.glob('*.gem').last
         
     | 
| 
       64 
64 
     | 
    
         | 
| 
       65 
65 
     | 
    
         
             
              current_ruby = `asdf current ruby`.match(/(\d.\d+.\d+)/)[1]
         
     | 
| 
       66 
66 
     | 
    
         | 
| 
       67 
     | 
    
         
            -
              `asdf list ruby`.split.map { |ruby| ruby.strip.sub(/^*/,  
     | 
| 
      
 67 
     | 
    
         
            +
              `asdf list ruby`.split.map { |ruby| ruby.strip.sub(/^*/, '') }.each do |ruby|
         
     | 
| 
       68 
68 
     | 
    
         
             
                `asdf shell ruby #{ruby}`
         
     | 
| 
       69 
69 
     | 
    
         
             
                puts `gem install #{file}`
         
     | 
| 
       70 
70 
     | 
    
         
             
              end
         
     | 
| 
         @@ -72,10 +72,10 @@ task :install do 
     | 
|
| 
       72 
72 
     | 
    
         
             
              `asdf shell ruby #{current_ruby}`
         
     | 
| 
       73 
73 
     | 
    
         
             
            end
         
     | 
| 
       74 
74 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
            desc  
     | 
| 
      
 75 
     | 
    
         
            +
            desc 'Development version check'
         
     | 
| 
       76 
76 
     | 
    
         
             
            task :ver do
         
     | 
| 
       77 
77 
     | 
    
         
             
              gver = `git ver`
         
     | 
| 
       78 
     | 
    
         
            -
              cver = IO.read(File.join(File.dirname(__FILE__),  
     | 
| 
      
 78 
     | 
    
         
            +
              cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
         
     | 
| 
       79 
79 
     | 
    
         
             
              res = `grep VERSION lib/na/version.rb`
         
     | 
| 
       80 
80 
     | 
    
         
             
              version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
         
     | 
| 
       81 
81 
     | 
    
         
             
              puts "git tag: #{gver}"
         
     | 
| 
         @@ -83,22 +83,22 @@ task :ver do 
     | 
|
| 
       83 
83 
     | 
    
         
             
              puts "changelog: #{cver}"
         
     | 
| 
       84 
84 
     | 
    
         
             
            end
         
     | 
| 
       85 
85 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
            desc  
     | 
| 
      
 86 
     | 
    
         
            +
            desc 'Changelog version check'
         
     | 
| 
       87 
87 
     | 
    
         
             
            task :cver do
         
     | 
| 
       88 
     | 
    
         
            -
              puts IO.read(File.join(File.dirname(__FILE__),  
     | 
| 
      
 88 
     | 
    
         
            +
              puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
         
     | 
| 
       89 
89 
     | 
    
         
             
            end
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
            desc  
     | 
| 
      
 91 
     | 
    
         
            +
            desc 'Bump incremental version number'
         
     | 
| 
       92 
92 
     | 
    
         
             
            task :bump, :type do |_, args|
         
     | 
| 
       93 
     | 
    
         
            -
              args.with_defaults(type:  
     | 
| 
       94 
     | 
    
         
            -
              version_file =  
     | 
| 
      
 93 
     | 
    
         
            +
              args.with_defaults(type: 'inc')
         
     | 
| 
      
 94 
     | 
    
         
            +
              version_file = 'lib/na/version.rb'
         
     | 
| 
       95 
95 
     | 
    
         
             
              content = IO.read(version_file)
         
     | 
| 
       96 
96 
     | 
    
         
             
              content.sub!(/VERSION = '(?<major>\d+)\.(?<minor>\d+)\.(?<inc>\d+)(?<pre>\S+)?'/) do
         
     | 
| 
       97 
97 
     | 
    
         
             
                m = Regexp.last_match
         
     | 
| 
       98 
     | 
    
         
            -
                major = m[ 
     | 
| 
       99 
     | 
    
         
            -
                minor = m[ 
     | 
| 
       100 
     | 
    
         
            -
                inc = m[ 
     | 
| 
       101 
     | 
    
         
            -
                pre = m[ 
     | 
| 
      
 98 
     | 
    
         
            +
                major = m['major'].to_i
         
     | 
| 
      
 99 
     | 
    
         
            +
                minor = m['minor'].to_i
         
     | 
| 
      
 100 
     | 
    
         
            +
                inc = m['inc'].to_i
         
     | 
| 
      
 101 
     | 
    
         
            +
                pre = m['pre']
         
     | 
| 
       102 
102 
     | 
    
         | 
| 
       103 
103 
     | 
    
         
             
                case args[:type]
         
     | 
| 
       104 
104 
     | 
    
         
             
                when /^maj/
         
     | 
| 
         @@ -115,73 +115,73 @@ task :bump, :type do |_, args| 
     | 
|
| 
       115 
115 
     | 
    
         
             
                $stdout.puts "At version #{major}.#{minor}.#{inc}#{pre}"
         
     | 
| 
       116 
116 
     | 
    
         
             
                "VERSION = '#{major}.#{minor}.#{inc}#{pre}'"
         
     | 
| 
       117 
117 
     | 
    
         
             
              end
         
     | 
| 
       118 
     | 
    
         
            -
              File.open(version_file,  
     | 
| 
      
 118 
     | 
    
         
            +
              File.open(version_file, 'w+') { |f| f.puts content }
         
     | 
| 
       119 
119 
     | 
    
         
             
            end
         
     | 
| 
       120 
120 
     | 
    
         | 
| 
       121 
121 
     | 
    
         
             
            # task default: %i[test clobber package]
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
            desc  
     | 
| 
      
 123 
     | 
    
         
            +
            desc 'Remove packages'
         
     | 
| 
       124 
124 
     | 
    
         
             
            task :clobber_packages do
         
     | 
| 
       125 
     | 
    
         
            -
              FileUtils.rm_f  
     | 
| 
      
 125 
     | 
    
         
            +
              FileUtils.rm_f 'pkg/*'
         
     | 
| 
       126 
126 
     | 
    
         
             
            end
         
     | 
| 
       127 
127 
     | 
    
         
             
            # Make a prerequisite of the preexisting clobber task
         
     | 
| 
       128 
     | 
    
         
            -
            desc  
     | 
| 
      
 128 
     | 
    
         
            +
            desc 'Clobber files'
         
     | 
| 
       129 
129 
     | 
    
         
             
            task clobber: :clobber_packages
         
     | 
| 
       130 
130 
     | 
    
         | 
| 
       131 
     | 
    
         
            -
            desc  
     | 
| 
      
 131 
     | 
    
         
            +
            desc 'Get Script Version'
         
     | 
| 
       132 
132 
     | 
    
         
             
            task :sver do
         
     | 
| 
       133 
133 
     | 
    
         
             
              res = `grep VERSION lib/na/version.rb`
         
     | 
| 
       134 
134 
     | 
    
         
             
              version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
         
     | 
| 
       135 
135 
     | 
    
         
             
              print version
         
     | 
| 
       136 
136 
     | 
    
         
             
            end
         
     | 
| 
       137 
137 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
            desc  
     | 
| 
      
 138 
     | 
    
         
            +
            desc 'Run tests in Docker'
         
     | 
| 
       139 
139 
     | 
    
         
             
            task :dockertest, :version, :login, :attempt do |_, args|
         
     | 
| 
       140 
     | 
    
         
            -
              args.with_defaults(version:  
     | 
| 
      
 140 
     | 
    
         
            +
              args.with_defaults(version: 'all', login: false, attempt: 1)
         
     | 
| 
       141 
141 
     | 
    
         
             
              `open -a Docker`
         
     | 
| 
       142 
142 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       144 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       145 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       146 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
      
 143 
     | 
    
         
            +
              Rake::Task['clobber'].reenable
         
     | 
| 
      
 144 
     | 
    
         
            +
              Rake::Task['clobber'].invoke
         
     | 
| 
      
 145 
     | 
    
         
            +
              Rake::Task['build'].reenable
         
     | 
| 
      
 146 
     | 
    
         
            +
              Rake::Task['build'].invoke
         
     | 
| 
       147 
147 
     | 
    
         | 
| 
       148 
148 
     | 
    
         
             
              case args[:version]
         
     | 
| 
       149 
149 
     | 
    
         
             
              when /^a/
         
     | 
| 
       150 
150 
     | 
    
         
             
                %w[6 7 3].each do |v|
         
     | 
| 
       151 
     | 
    
         
            -
                  Rake::Task[ 
     | 
| 
       152 
     | 
    
         
            -
                  Rake::Task[ 
     | 
| 
      
 151 
     | 
    
         
            +
                  Rake::Task['dockertest'].reenable
         
     | 
| 
      
 152 
     | 
    
         
            +
                  Rake::Task['dockertest'].invoke(v, false)
         
     | 
| 
       153 
153 
     | 
    
         
             
                end
         
     | 
| 
       154 
154 
     | 
    
         
             
                Process.exit 0
         
     | 
| 
       155 
155 
     | 
    
         
             
              when /^3\.?3/
         
     | 
| 
       156 
     | 
    
         
            -
                img =  
     | 
| 
       157 
     | 
    
         
            -
                file =  
     | 
| 
      
 156 
     | 
    
         
            +
                img = 'natest33'
         
     | 
| 
      
 157 
     | 
    
         
            +
                file = 'docker/Dockerfile-3.3'
         
     | 
| 
       158 
158 
     | 
    
         
             
              when /^3/
         
     | 
| 
       159 
     | 
    
         
            -
                version =  
     | 
| 
       160 
     | 
    
         
            -
                img =  
     | 
| 
       161 
     | 
    
         
            -
                file =  
     | 
| 
      
 159 
     | 
    
         
            +
                version = '3.0'
         
     | 
| 
      
 160 
     | 
    
         
            +
                img = 'natest3'
         
     | 
| 
      
 161 
     | 
    
         
            +
                file = 'docker/Dockerfile-3.0'
         
     | 
| 
       162 
162 
     | 
    
         
             
              when /6$/
         
     | 
| 
       163 
     | 
    
         
            -
                version =  
     | 
| 
       164 
     | 
    
         
            -
                img =  
     | 
| 
       165 
     | 
    
         
            -
                file =  
     | 
| 
      
 163 
     | 
    
         
            +
                version = '2.6'
         
     | 
| 
      
 164 
     | 
    
         
            +
                img = 'natest26'
         
     | 
| 
      
 165 
     | 
    
         
            +
                file = 'docker/Dockerfile-2.6'
         
     | 
| 
       166 
166 
     | 
    
         
             
              when /(^2|7$)/
         
     | 
| 
       167 
     | 
    
         
            -
                version =  
     | 
| 
       168 
     | 
    
         
            -
                img =  
     | 
| 
       169 
     | 
    
         
            -
                file =  
     | 
| 
      
 167 
     | 
    
         
            +
                version = '2.7'
         
     | 
| 
      
 168 
     | 
    
         
            +
                img = 'natest27'
         
     | 
| 
      
 169 
     | 
    
         
            +
                file = 'docker/Dockerfile-2.7'
         
     | 
| 
       170 
170 
     | 
    
         
             
              else
         
     | 
| 
       171 
     | 
    
         
            -
                version =  
     | 
| 
       172 
     | 
    
         
            -
                img =  
     | 
| 
       173 
     | 
    
         
            -
                file =  
     | 
| 
      
 171 
     | 
    
         
            +
                version = '3.0.1'
         
     | 
| 
      
 172 
     | 
    
         
            +
                img = 'natest'
         
     | 
| 
      
 173 
     | 
    
         
            +
                file = 'docker/Dockerfile'
         
     | 
| 
       174 
174 
     | 
    
         
             
              end
         
     | 
| 
       175 
175 
     | 
    
         | 
| 
       176 
176 
     | 
    
         
             
              puts `docker build . --file #{file} -t #{img}`
         
     | 
| 
       177 
177 
     | 
    
         | 
| 
       178 
     | 
    
         
            -
              raise DockerError,  
     | 
| 
      
 178 
     | 
    
         
            +
              raise DockerError, 'Error building docker image' unless $CHILD_STATUS.success?
         
     | 
| 
       179 
179 
     | 
    
         | 
| 
       180 
180 
     | 
    
         
             
              dirs = {
         
     | 
| 
       181 
     | 
    
         
            -
                File.dirname(__FILE__) =>  
     | 
| 
       182 
     | 
    
         
            -
                File.expand_path( 
     | 
| 
      
 181 
     | 
    
         
            +
                File.dirname(__FILE__) => '/na',
         
     | 
| 
      
 182 
     | 
    
         
            +
                File.expand_path('~/.config') => '/root/.config'
         
     | 
| 
       183 
183 
     | 
    
         
             
              }
         
     | 
| 
       184 
     | 
    
         
            -
              dir_args = dirs.map { |s, d| " -v '#{s}:#{d}'" }.join( 
     | 
| 
      
 184 
     | 
    
         
            +
              dir_args = dirs.map { |s, d| " -v '#{s}:#{d}'" }.join(' ')
         
     | 
| 
       185 
185 
     | 
    
         
             
              exec "docker run #{dir_args} -it #{img} /bin/bash -l" if args[:login]
         
     | 
| 
       186 
186 
     | 
    
         | 
| 
       187 
187 
     | 
    
         
             
              spinner = TTY::Spinner.new("[:spinner] Running tests (#{version})...", hide_cursor: true)
         
     | 
| 
         @@ -197,15 +197,15 @@ task :dockertest, :version, :login, :attempt do |_, args| 
     | 
|
| 
       197 
197 
     | 
    
         
             
              # puts res
         
     | 
| 
       198 
198 
     | 
    
         
             
              # puts commit&.empty? ? "Error commiting Docker tag #{img}" : "Committed Docker tag #{img}"
         
     | 
| 
       199 
199 
     | 
    
         
             
            rescue DockerError
         
     | 
| 
       200 
     | 
    
         
            -
              raise StandardError.new( 
     | 
| 
      
 200 
     | 
    
         
            +
              raise StandardError.new('Docker not responding') if args[:attempt] > 3
         
     | 
| 
       201 
201 
     | 
    
         | 
| 
       202 
202 
     | 
    
         
             
              `open -a Docker`
         
     | 
| 
       203 
203 
     | 
    
         
             
              sleep 3
         
     | 
| 
       204 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
       205 
     | 
    
         
            -
              Rake::Task[ 
     | 
| 
      
 204 
     | 
    
         
            +
              Rake::Task['dockertest'].reenable
         
     | 
| 
      
 205 
     | 
    
         
            +
              Rake::Task['dockertest'].invoke(args[:version], args[:login], args[:attempt] + 1)
         
     | 
| 
       206 
206 
     | 
    
         
             
            end
         
     | 
| 
       207 
207 
     | 
    
         | 
| 
       208 
     | 
    
         
            -
            desc  
     | 
| 
      
 208 
     | 
    
         
            +
            desc 'alias for build'
         
     | 
| 
       209 
209 
     | 
    
         
             
            task package: :build
         
     | 
| 
       210 
210 
     | 
    
         | 
| 
       211 
211 
     | 
    
         
             
            desc 'Run tests with coverage'
         
     | 
    
        data/bin/commands/add.rb
    CHANGED
    
    | 
         @@ -11,6 +11,17 @@ class App 
     | 
|
| 
       11 
11 
     | 
    
         
             
              allow you to pick to which file the action gets added.'
         
     | 
| 
       12 
12 
     | 
    
         
             
              arg_name "ACTION"
         
     | 
| 
       13 
13 
     | 
    
         
             
              command :add do |c|
         
     | 
| 
      
 14 
     | 
    
         
            +
                c.desc "Started time (natural language or ISO)"
         
     | 
| 
      
 15 
     | 
    
         
            +
                c.arg_name "DATE"
         
     | 
| 
      
 16 
     | 
    
         
            +
                c.flag %i[started], type: :date_begin
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                c.desc "End/Finished time (natural language or ISO)"
         
     | 
| 
      
 19 
     | 
    
         
            +
                c.arg_name "DATE"
         
     | 
| 
      
 20 
     | 
    
         
            +
                c.flag %i[end finished], type: :date_end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                c.desc "Duration (e.g. 45m, 2h, 1d2h30m, or minutes)"
         
     | 
| 
      
 23 
     | 
    
         
            +
                c.arg_name "DURATION"
         
     | 
| 
      
 24 
     | 
    
         
            +
                c.flag %i[duration], type: :duration
         
     | 
| 
       14 
25 
     | 
    
         
             
                c.example 'na add "A cool feature I thought of @idea"', desc: "Add a new action to the Inbox, including a tag"
         
     | 
| 
       15 
26 
     | 
    
         
             
                c.example 'na add "A bug I need to fix" -p 4 -n',
         
     | 
| 
       16 
27 
     | 
    
         
             
                          desc: "Add a new action to the Inbox, set its @priority to 4, and prompt for an additional note."
         
     | 
| 
         @@ -181,7 +192,26 @@ class App 
     | 
|
| 
       181 
192 
     | 
    
         
             
                  note.<< split_note unless split_note.nil?
         
     | 
| 
       182 
193 
     | 
    
         
             
                  note.concat(line_note) unless line_note.nil?
         
     | 
| 
       183 
194 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
                   
     | 
| 
      
 195 
     | 
    
         
            +
                  # Compute started/done based on flags
         
     | 
| 
      
 196 
     | 
    
         
            +
                  started_at = options[:started]
         
     | 
| 
      
 197 
     | 
    
         
            +
                  started_at = NA::Types.parse_date_begin(started_at) if started_at && !started_at.is_a?(Time)
         
     | 
| 
      
 198 
     | 
    
         
            +
                  done_at = options[:end] || options[:finished]
         
     | 
| 
      
 199 
     | 
    
         
            +
                  done_at = NA::Types.parse_date_end(done_at) if done_at && !done_at.is_a?(Time)
         
     | 
| 
      
 200 
     | 
    
         
            +
                  duration_seconds = options[:duration]
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  NA.notify("ADD parsed started_at=#{started_at.inspect} done_at=#{done_at.inspect} duration=#{duration_seconds.inspect}", debug: true)
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                  # Ensure @started is present in the action text if a start time was provided
         
     | 
| 
      
 205 
     | 
    
         
            +
                  if started_at
         
     | 
| 
      
 206 
     | 
    
         
            +
                    started_str = started_at.strftime('%Y-%m-%d %H:%M')
         
     | 
| 
      
 207 
     | 
    
         
            +
                    # remove any existing @start/@started tag before appending
         
     | 
| 
      
 208 
     | 
    
         
            +
                    action = action.gsub(/(?<=\A| )@start(?:ed)?\(.*?\)/i, '').strip
         
     | 
| 
      
 209 
     | 
    
         
            +
                    action = "#{action} @started(#{started_str})"
         
     | 
| 
      
 210 
     | 
    
         
            +
                  end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                  NA.add_action(target, options[:project], action, note,
         
     | 
| 
      
 213 
     | 
    
         
            +
                                finish: options[:finish], append: append,
         
     | 
| 
      
 214 
     | 
    
         
            +
                                started_at: started_at, done_at: done_at, duration_seconds: duration_seconds)
         
     | 
| 
       185 
215 
     | 
    
         
             
                end
         
     | 
| 
       186 
216 
     | 
    
         
             
              end
         
     | 
| 
       187 
217 
     | 
    
         
             
            end
         
     |