na 1.2.94 → 1.2.95

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be595b84ced2283de971f650c8a7fcc906fe3f85afb632d28844226948b2e04a
4
- data.tar.gz: 42c3e923bc87a57ce003a1f09abbb36fbe50ae7a8cb0f53ba61a39cfe396b26f
3
+ metadata.gz: 8a56c16c9c040f54facd2d6b9fffbf6740e168a63099a77ba15fb90c122ba8a2
4
+ data.tar.gz: 624665b0fc50ac0d251ca6d464899050351f52aadf37a05e154e3e190fc117b6
5
5
  SHA512:
6
- metadata.gz: a58da516e33145ca50285e373c62d2cee940fc477027f8cbcbb17035a3105298ca69c29a2d7f50fcc7154e545fa31999cf0e4c19fca17d974b8804de21538c32
7
- data.tar.gz: 53bc7323992af3458ca1b797c82e25a47fa4d3d0fcff842575795eb23606b6a405628e8e8ed83c7f8bb177074b72bc763c01e09136888864752b92e3cfa9c5e1
6
+ metadata.gz: ab7436edfa9b1edb36a93b68a11966955168a14d0834a178bd90744d4f2333a95aea9d7a03c46fed8dd862d2378fcb6af5b89ff3be39502828d5287617dc9cff
7
+ data.tar.gz: 2611cb0295cbceba368e418f3502148c1d2748a2515eab3fbaad90fca3d9c05cb654865aa84a050b8d0449f6c41378779e27f268e118d927497c0a6db85d6cc4
@@ -1,8 +1,11 @@
1
- Write a commmit message that uses @ labels to specify what type of change each line is. Apply @new, @fixed, @changed, @improved, and @breaking as appropriate to each line. Only add @ labels to changes that affect the user, not technical details. Technical details can be included in the commit, just don't add @ labels to those lines. Be sure to include a general description (< 60 characters) as the first line, followed by a line break.
1
+ Review all staged and unstaged files in the repo. Write a commmit message that uses @ labels to specify what type of change each line is. Apply @new, @fixed, @changed, @improved, and @breaking as appropriate to each line. Only add @ labels to changes that affect the user, not technical details. Technical details can be included in the commit, just don't add @ labels to those lines. Be sure to include a general description (< 60 characters) as the first line, followed by a line break.
2
2
 
3
3
  Do not add @tags to notes about documentation updates. Always focus on actual code changes we've made since the last commit when generating the commit message.
4
4
 
5
+ Always use straight quotes and ascii punctuation, never curl quotes. Don't use emoji.
6
+
7
+ Always include a blank line after the first line (commit message) before the note.
5
8
 
6
9
  Save this commit message to commit_message.txt. Overwrite existing contents.
7
10
 
8
- Save this commit message to commit_message.txt{% if args.reset %}. Overwrite existing contents.{% else %}. Update the file, merging changes, if file exists, otherwise create new.{% endif %}
11
+ Save this commit message to commit_message.txt{% if args.reset or args.replace %}. Overwrite existing contents.{% else %}. Update the file, merging changes, if file exists, otherwise create new.{% endif %}
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-11-16 16:32:01 UTC using RuboCop version 1.81.7.
3
+ # on 2025-12-03 13:48:16 UTC using RuboCop version 1.81.7.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -21,18 +21,18 @@ Lint/UnusedMethodArgument:
21
21
  Exclude:
22
22
  - 'lib/na/string.rb'
23
23
 
24
- # Offense count: 49
24
+ # Offense count: 58
25
25
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
26
26
  Metrics/AbcSize:
27
27
  Max: 309
28
28
 
29
- # Offense count: 11
29
+ # Offense count: 14
30
30
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
31
31
  # AllowedMethods: refine
32
32
  Metrics/BlockLength:
33
- Max: 159
33
+ Max: 186
34
34
 
35
- # Offense count: 6
35
+ # Offense count: 7
36
36
  # Configuration parameters: CountBlocks, CountModifierForms.
37
37
  Metrics/BlockNesting:
38
38
  Max: 4
@@ -40,29 +40,29 @@ Metrics/BlockNesting:
40
40
  # Offense count: 6
41
41
  # Configuration parameters: CountComments, CountAsOne.
42
42
  Metrics/ClassLength:
43
- Max: 926
43
+ Max: 1492
44
44
 
45
- # Offense count: 34
45
+ # Offense count: 41
46
46
  # Configuration parameters: AllowedMethods, AllowedPatterns.
47
47
  Metrics/CyclomaticComplexity:
48
48
  Max: 91
49
49
 
50
- # Offense count: 55
50
+ # Offense count: 63
51
51
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
52
52
  Metrics/MethodLength:
53
- Max: 202
53
+ Max: 239
54
54
 
55
55
  # Offense count: 5
56
56
  # Configuration parameters: CountComments, CountAsOne.
57
57
  Metrics/ModuleLength:
58
- Max: 928
58
+ Max: 1494
59
59
 
60
60
  # Offense count: 5
61
61
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
62
62
  Metrics/ParameterLists:
63
63
  Max: 23
64
64
 
65
- # Offense count: 33
65
+ # Offense count: 39
66
66
  # Configuration parameters: AllowedMethods, AllowedPatterns.
67
67
  Metrics/PerceivedComplexity:
68
68
  Max: 104
@@ -81,6 +81,13 @@ Security/YAMLLoad:
81
81
  - 'lib/na/next_action.rb'
82
82
  - 'lib/na/theme.rb'
83
83
 
84
+ # Offense count: 1
85
+ # This cop supports unsafe autocorrection (--autocorrect-all).
86
+ # Configuration parameters: MinBranchesCount.
87
+ Style/CaseLikeIf:
88
+ Exclude:
89
+ - 'lib/na/next_action.rb'
90
+
84
91
  # Offense count: 2
85
92
  # This cop supports safe autocorrection (--autocorrect).
86
93
  Style/IfUnlessModifier:
@@ -88,6 +95,14 @@ Style/IfUnlessModifier:
88
95
  - 'lib/na/benchmark.rb'
89
96
  - 'lib/na/colors.rb'
90
97
 
98
+ # Offense count: 2
99
+ # This cop supports safe autocorrection (--autocorrect).
100
+ # Configuration parameters: EnforcedStyle.
101
+ # SupportedStyles: line_count_dependent, lambda, literal
102
+ Style/Lambda:
103
+ Exclude:
104
+ - 'lib/na/next_action.rb'
105
+
91
106
  # Offense count: 1
92
107
  # This cop supports unsafe autocorrection (--autocorrect-all).
93
108
  # Configuration parameters: EnforcedStyle, Autocorrect.
@@ -96,6 +111,13 @@ Style/ModuleFunction:
96
111
  Exclude:
97
112
  - 'lib/na/colors.rb'
98
113
 
114
+ # Offense count: 4
115
+ # This cop supports safe autocorrection (--autocorrect).
116
+ # Configuration parameters: AllowMethodComparison, ComparisonsThreshold.
117
+ Style/MultipleComparison:
118
+ Exclude:
119
+ - 'lib/na/next_action.rb'
120
+
99
121
  # Offense count: 1
100
122
  # This cop supports safe autocorrection (--autocorrect).
101
123
  # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
@@ -118,7 +140,7 @@ Style/YAMLFileRead:
118
140
  Exclude:
119
141
  - 'lib/na/theme.rb'
120
142
 
121
- # Offense count: 28
143
+ # Offense count: 38
122
144
  # This cop supports safe autocorrection (--autocorrect).
123
145
  # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
124
146
  # URISchemes: http, https
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ### 1.2.95
2
+
3
+ 2025-12-03 08:23
4
+
5
+ #### NEW
6
+
7
+ - Support TaskPaper @search() syntax in `na find`, `na saved`, and `na next` queries.
8
+ - Allow TaskPaper-style item paths (`/Inbox//Subproject`, `/*`) for targeting projects in `add --to`, `next --project`, and `find --project`.
9
+ - Discover TaskPaper saved searches from any `TITLE @search(...)` line in todo files, regardless of project name.
10
+ - Support TaskPaper type shortcuts (project, task, note) in @search() queries.
11
+ - Allow slicing syntax ([index], [start:end], etc.) in TaskPaper @search() to limit results, including parenthesized expressions.
12
+
13
+ #### IMPROVED
14
+
15
+ - Saved searches from `saved_searches.yml` now recognize TaskPaper `@search(...)` values and execute them via the TaskPaper search engine instead of shelling commands.
16
+ - TaskPaper `@search(...)` supports nested boolean expressions with `and`/`or`, tag predicates, `@text`, project filters, and `@done` handling.
17
+ - Item-path filters in `@search(...)` restrict results to matching project subtrees (e.g. `/Inbox//Bugs and not @done`).
18
+ - `na saved` and `na find` now combine TaskPaper and YAML saved searches, matching names across both sources.
19
+ - TaskPaper typei shortcuts `project`, `task`, and `note` are interpreted in @search() expressions (e.g. `project Inbox` => `project = "Inbox"`).
20
+ - TaskPaper @search() item paths integrate with next/find/saved and respect project hierarchies.
21
+ - Tag-only predicates like @priority now match tags with any value (for example @priority(3)).
22
+ - Na next/find/saved TaskPaper queries share the same evaluation engine, keeping behavior consistent across commands.
23
+
1
24
  ### 1.2.94
2
25
 
3
26
  2025-12-01 09:22
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.94)
4
+ na (1.2.95)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  csv (>= 3.2)
7
7
  git (~> 3.0.0)
@@ -15,7 +15,7 @@ PATH
15
15
  GEM
16
16
  remote: https://rubygems.org/
17
17
  specs:
18
- activesupport (8.1.0)
18
+ activesupport (8.1.1)
19
19
  base64
20
20
  bigdecimal
21
21
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -28,15 +28,15 @@ GEM
28
28
  securerandom (>= 0.3)
29
29
  tzinfo (~> 2.0, >= 2.0.5)
30
30
  uri (>= 0.13.1)
31
- addressable (2.8.7)
32
- public_suffix (>= 2.0.2, < 7.0)
31
+ addressable (2.8.8)
32
+ public_suffix (>= 2.0.2, < 8.0)
33
33
  ast (2.4.3)
34
34
  base64 (0.3.0)
35
35
  bigdecimal (3.3.1)
36
36
  bump (0.6.1)
37
37
  chronic (0.10.2)
38
38
  concurrent-ruby (1.3.5)
39
- connection_pool (2.5.4)
39
+ connection_pool (2.5.5)
40
40
  csv (3.3.5)
41
41
  diff-lcs (1.6.2)
42
42
  docile (1.4.1)
@@ -49,40 +49,40 @@ GEM
49
49
  gli (2.21.5)
50
50
  i18n (1.14.7)
51
51
  concurrent-ruby (~> 1.0)
52
- json (2.15.2)
52
+ json (2.16.0)
53
53
  language_server-protocol (3.17.0.5)
54
54
  lint_roller (1.1.0)
55
55
  logger (1.7.0)
56
56
  mdless (1.0.37)
57
- minitest (5.26.0)
57
+ minitest (5.26.2)
58
58
  ostruct (0.6.3)
59
59
  parallel (1.27.0)
60
- parser (3.3.8.0)
60
+ parser (3.3.10.0)
61
61
  ast (~> 2.4.1)
62
62
  racc
63
63
  prism (1.6.0)
64
64
  process_executer (1.3.0)
65
- public_suffix (6.0.2)
65
+ public_suffix (7.0.0)
66
66
  racc (1.8.1)
67
67
  rainbow (3.1.1)
68
- rake (13.3.0)
68
+ rake (13.3.1)
69
69
  rchardet (1.10.0)
70
70
  rdoc (4.3.0)
71
- regexp_parser (2.10.0)
72
- rspec (3.13.0)
71
+ regexp_parser (2.11.3)
72
+ rspec (3.13.2)
73
73
  rspec-core (~> 3.13.0)
74
74
  rspec-expectations (~> 3.13.0)
75
75
  rspec-mocks (~> 3.13.0)
76
- rspec-core (3.13.3)
76
+ rspec-core (3.13.6)
77
77
  rspec-support (~> 3.13.0)
78
78
  rspec-expectations (3.13.5)
79
79
  diff-lcs (>= 1.2.0, < 2.0)
80
80
  rspec-support (~> 3.13.0)
81
- rspec-mocks (3.13.4)
81
+ rspec-mocks (3.13.7)
82
82
  diff-lcs (>= 1.2.0, < 2.0)
83
83
  rspec-support (~> 3.13.0)
84
84
  rspec-support (3.13.6)
85
- rubocop (1.75.7)
85
+ rubocop (1.81.7)
86
86
  json (~> 2.3)
87
87
  language_server-protocol (~> 3.17.0.2)
88
88
  lint_roller (~> 1.1.0)
@@ -90,16 +90,16 @@ GEM
90
90
  parser (>= 3.3.0.2)
91
91
  rainbow (>= 2.2.2, < 4.0)
92
92
  regexp_parser (>= 2.9.3, < 3.0)
93
- rubocop-ast (>= 1.44.0, < 2.0)
93
+ rubocop-ast (>= 1.47.1, < 2.0)
94
94
  ruby-progressbar (~> 1.7)
95
95
  unicode-display_width (>= 2.4.0, < 4.0)
96
- rubocop-ast (1.44.1)
96
+ rubocop-ast (1.48.0)
97
97
  parser (>= 3.3.7.2)
98
98
  prism (~> 1.4)
99
- rubocop-performance (1.25.0)
99
+ rubocop-performance (1.26.1)
100
100
  lint_roller (~> 1.1)
101
101
  rubocop (>= 1.75.0, < 2.0)
102
- rubocop-ast (>= 1.38.0, < 2.0)
102
+ rubocop-ast (>= 1.47.1, < 2.0)
103
103
  ruby-progressbar (1.13.0)
104
104
  securerandom (0.4.1)
105
105
  simplecov (0.22.0)
@@ -119,10 +119,10 @@ GEM
119
119
  tty-which (0.5.0)
120
120
  tzinfo (2.0.6)
121
121
  concurrent-ruby (~> 1.0)
122
- unicode-display_width (3.1.4)
123
- unicode-emoji (~> 4.0, >= 4.0.4)
124
- unicode-emoji (4.0.4)
125
- uri (1.0.4)
122
+ unicode-display_width (3.2.0)
123
+ unicode-emoji (~> 4.1)
124
+ unicode-emoji (4.1.0)
125
+ uri (1.1.1)
126
126
  wisper (2.0.1)
127
127
 
128
128
  PLATFORMS
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.94.
12
+ The current version of `na` is 1.2.95.
13
13
 
14
14
 
15
15
  ### Table of contents
@@ -22,6 +22,7 @@ The current version of `na` is 1.2.94.
22
22
  - [Adding todos](#adding-todos)
23
23
  - [Updating todos](#updating-todos)
24
24
  - [Terminology](#terminology)
25
+ - [TaskPaper Syntax](#taskpaper-syntax)
25
26
  - [Usage](#usage)
26
27
  - [Commands](#commands)
27
28
  - [add](#add)
@@ -106,6 +107,146 @@ You can mark todos as complete, delete them, add and remove tags, change priorit
106
107
 
107
108
  **Note**: Refers to lines appearing between action lines that start without hyphens. The note is attached to the preceding action regardless of indentation.
108
109
 
110
+ ### TaskPaper Syntax
111
+
112
+ NA has its own syntax for searching tags, titles, and notes, but it also understands most of the [TaskPaper query syntax](https://guide.taskpaper.com/reference/searches/). TaskPaper-style searches are accepted anywhere you can pass a `@search(...)` expression, including:
113
+
114
+ - `na next "@search(...)"` (or via saved searches)
115
+ - `na find "@search(...)"` and `na saved NAME` when the saved value is `@search(...)`
116
+ - TaskPaper files themselves, on any line of the form `TITLE @search(PARAMS)` (these become runnable saved searches)
117
+
118
+ What follows documents the subset and extensions of TaskPaper search that `na` supports.
119
+
120
+ #### Predicates and tag searches
121
+
122
+ - **Simple text**: Bare words are treated as plain-text search tokens on the action line, combined with `and` by default.
123
+ - **Tag predicates**:
124
+ - `@tag` (no value) means "this tag exists with any value". For example, `@priority` matches `@priority(1)`, `@priority(3)`, etc.
125
+ - `@tag op VALUE` uses the following operators (TaskPaper relations are mapped to NA's comparison codes):
126
+ - `=` / `==` / no operator: equality
127
+ - `!=`: inequality (implemented as a negated equality)
128
+ - `<`, `>`, `<=`, `>=`: numeric or date comparison when the value parses as a time or number
129
+ - `contains` ??? `*=`: value substring match
130
+ - `beginswith` ??? `^=`: prefix match
131
+ - `endswith` ??? `$=`: suffix match
132
+ - `matches` ??? `=~`: regular expression match (case-insensitive)
133
+ - Relation modifiers like `[i]`, `[s]`, `[n]`, `[d]`, `[l]` are parsed and discarded; matching is always case-insensitive for string comparisons.
134
+ - **`@text`**:
135
+ - `@text "foo"` is treated as a plain-text predicate on the action line (equivalent to searching for `"foo"`).
136
+ - `@text` without a value is ignored.
137
+ - **`@done`**:
138
+ - `@done` sets an internal "include done" flag and is not treated as a normal tag filter.
139
+ - `not @done` (or `@done` combined with other predicates) correctly toggles whether completed actions are included.
140
+
141
+ #### Project predicates
142
+
143
+ - **Project equality**:
144
+ - `project = "Inbox"` or `project == "Inbox"` limits results to actions under the `Inbox` project (matching NA's project path).
145
+ - `project != "Archive"` excludes actions whose project chain ends in `Archive`.
146
+ - **Shortcuts**:
147
+ - `project NAME` at the start of an expression is treated as a shortcut:
148
+ - If it is the entire predicate, it becomes `project = "NAME"`.
149
+ - If it is followed by additional logic, e.g. `project Inbox and @na`, the leading `NAME and` is dropped and the rest (`@na ...`) is parsed as normal. This matches TaskPaper's "type shortcut" usage rather than combining project name and text search.
150
+
151
+ #### Type shortcuts
152
+
153
+ TaskPaper defines type shortcuts that expand to `@type = ... and`. NA supports them in a way that is practical for task searches:
154
+
155
+ - `project QUERY`:
156
+ - If used alone, becomes `project = "QUERY"`.
157
+ - If followed by more predicates joined by `and`/`or`, only the tail expression is used (e.g. `project Inbox and @na` becomes `@na`).
158
+ - `task QUERY` and `note QUERY`:
159
+ - The leading keyword is removed and the rest of the expression is interpreted as a normal text/tag predicate.
160
+ - NA does not currently distinguish task vs note types for filtering; these shortcuts are primarily syntactic sugar.
161
+
162
+ #### Boolean logic
163
+
164
+ - `and` and `or` are supported with parentheses for grouping:
165
+ - `@na and not @done`
166
+ - `(@priority >= 3 and @na) or @today`
167
+ - Expressions are parsed into an internal boolean AST and converted to disjunctive normal form (OR of AND-clauses) before evaluation, so:
168
+ - Complex nested groupings with multiple `and`/`or` operators behave as expected.
169
+ - The unary `not` operator is handled at the predicate level (e.g. `not @done`, `not @priority >= 3`).
170
+
171
+ #### Item paths
172
+
173
+ NA understands a subset of TaskPaper item-path syntax and maps it to project scopes in your todo files. Item paths can be used:
174
+
175
+ - As the leading part of a `@search(...)` expression:
176
+ - `@search(/Inbox//Bugs and not @done)`
177
+ - In TaskPaper saved searches (`TITLE @search(/Inbox//Project A and @na)`).
178
+
179
+ Supported item-path features:
180
+
181
+ - **Axes**:
182
+ - `/Name` selects top-level items whose title contains `Name`.
183
+ - `//Name` selects any descendants (at any depth) whose title contains `Name`.
184
+ - **Wildcards**:
185
+ - `*` matches "all items" on that step.
186
+ - Example: `/*` selects all top-level projects; `/Inbox//*` selects everything under `Inbox`.
187
+ - **Semantics in NA**:
188
+ - Each matching project path is turned into an NA project chain like `"Inbox:New Videos"`.
189
+ - Actions are filtered post-parse so that only actions whose parent chain starts with one of the resolved project paths are returned.
190
+
191
+ Current limitations:
192
+
193
+ - Set operations such as `union`, `intersect`, and `except` are not yet implemented.
194
+ - Slicing on item-path steps (e.g. `project *//not @done[0]` where `[0]` is attached to a path step) is not yet interpreted; see "Slicing results" below for what is supported.
195
+
196
+ #### Slicing results
197
+
198
+ NA supports TaskPaper-style slicing on the **result set of a `@search(...)` expression**, not (yet) on individual item-path steps:
199
+
200
+ - Supported forms:
201
+ - `[index]`
202
+ - `[start:end]`
203
+ - `[start:]`
204
+ - `[:end]`
205
+ - `[:]`
206
+ - Examples:
207
+ - `@search((project Inbox and @na and not @done)[0])`:
208
+ - Evaluates the predicates, then returns only the first matching action.
209
+ - `@search(/Inbox//Bugs and @na and not @done[0])`:
210
+ - Restricts to the `Inbox/Bugs` subtree, then returns only the first incomplete `@na` action under that subtree.
211
+
212
+ Slice semantics:
213
+
214
+ - Slices are applied per clause after all tag/project/item-path filters:
215
+ - `[index]` ??? the single action at `index` (0-based) if it exists.
216
+ - `[start:end]` ??? actions in the half-open range `start...end`.
217
+ - `[start:]` ??? actions from `start` to the end.
218
+ - `[:end]` / `[:]` ??? from the beginning to `end` (or all actions).
219
+
220
+ #### Saved searches and TaskPaper files
221
+
222
+ - **YAML saved searches**:
223
+ - `~/.local/share/na/saved_searches.yml` values that are plain strings continue to use NA's original search syntax.
224
+ - Values of the form `@search(...)` are parsed using the TaskPaper engine described above and support the full feature set (predicates, boolean logic, item paths, slicing).
225
+ - **TaskPaper-embedded saved searches**:
226
+ - Any line in a `.taskpaper` file that matches:
227
+ - `TITLE @search(PARAMS)`
228
+ - is treated as a saved search. `TITLE` becomes the search name, and `PARAMS` is parsed with the same TaskPaper engine. These searches are available via `na saved TITLE` and can coexist with YAML definitions.
229
+
230
+ #### Supported vs. unsupported TaskPaper features
231
+
232
+ In summary, NA's TaskPaper support includes:
233
+
234
+ - Tag predicates with most TaskPaper relations and modifiers.
235
+ - `@text` predicates for plain-text search.
236
+ - `@done` handling wired into NA's "done" flag.
237
+ - `project` equality and exclusions, plus a practical "project" type shortcut.
238
+ - Boolean logic with `and`, `or`, `not`, and parentheses.
239
+ - Item paths with `/`, `//`, and `*` to scope searches by project hierarchy.
240
+ - Result slicing on entire `@search(...)` expressions.
241
+ - Integration with `next`, `find`, and `saved` via `@search(...)`, including YAML and TaskPaper-defined saved searches.
242
+
243
+ The following TaskPaper features are **not** yet implemented:
244
+
245
+ - Item-path set operations (`union`, `intersect`, `except`).
246
+ - Slicing applied directly to individual item-path steps (only whole-expression slicing is currently supported).
247
+
248
+ Where NA already provided its own search syntax (e.g. `na find`, `na tagged`), TaskPaper searches are additive: you can choose whichever is more convenient for a given query, and `@search(...)` expressions are routed through a common TaskPaper engine so behavior is consistent across commands.
249
+
109
250
  ### Usage
110
251
 
111
252
  ```
@@ -116,7 +257,7 @@ SYNOPSIS
116
257
  na [global options] command [command options] [arguments...]
117
258
 
118
259
  VERSION
119
- 1.2.94
260
+ 1.2.95
120
261
 
121
262
  GLOBAL OPTIONS
122
263
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -250,7 +391,7 @@ EXAMPLE
250
391
 
251
392
  Example: `na find cool feature idea`
252
393
 
253
- Unless `--exact` is specified, search is tokenized and combined with AND, so `na find cool feature idea` translates to `cool AND feature AND idea`, matching any string that contains all of the words. To make a token required and others optional, add a `+` before it (e.g. `cool +feature idea` is `(cool OR idea) AND feature`). Wildcards allowed (`*` and `?`), use `--regex` to interpret the search as a regular expression. Use `-v` to invert the results (display non-matching actions only).
394
+ Unless `--exact` is specified, search is tokenized and combined with AND, so `na find cool feature idea` translates to `cool AND feature AND idea`, matching any string that contains all of the words. To make a token required and others optional, add a `+` before it (e.g. `cool +feature idea` is `(cool OR idea) AND feature`). Wildcards allowed (`*` and `?`), use `--regex` to interpret the search as a regular expression. Use `-v` to invert the results (display non-matching actions only). Searches accept both NA search syntax and TaskPaper search syntax (see [TaskPaper search section](#taskpaper-syntax) above).
254
395
 
255
396
  ```
256
397
  NAME
@@ -367,7 +508,7 @@ Examples:
367
508
  - `na next -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
368
509
  - `na next marked2` (show next actions from another directory you've previously used na on)
369
510
 
370
- To see all next actions across all known todos, use `na next "*"`. You can combine multiple arguments to see actions across multiple todos, e.g. `na next marked nvultra`.
511
+ To see all next actions across all known todos, use `na next "*"`. You can combine multiple arguments to see actions across multiple todos, e.g. `na next marked nvultra`. Filters and search terms accept both NA search syntax and TaskPaper search syntax (see [TaskPaper search section](#taskpaper-syntax) above).
371
512
 
372
513
  ```
373
514
  NAME
@@ -563,7 +704,7 @@ The saved command runs saved searches. To save a search, add `--save SEARCH_NAME
563
704
 
564
705
  Search names can be partially matched when calling them, so if you have a search named "overdue," you can match it with `na saved over` (shortest match will be used).
565
706
 
566
- Run `na saved` without an argument to list your saved searches.
707
+ Run `na saved` without an argument to list your saved searches. Saved searches preserve whether NA search syntax or TaskPaper search syntax was used, and both syntaxes are supported when defining or running them (see [TaskPaper search section](#taskpaper-syntax) above).
567
708
 
568
709
  > As a shortcut, if `na` is run with one argument that matches the name of a saved search, it will execute that search, so running `na maybe` is the same as running `na saved maybe`.
569
710
 
data/bin/commands/add.rb CHANGED
@@ -209,7 +209,25 @@ class App
209
209
  action = "#{action} @started(#{started_str})"
210
210
  end
211
211
 
212
- NA.add_action(target, options[:project], action, note,
212
+ # Resolve TaskPaper-style item path in --to/--project if provided
213
+ project = options[:project]
214
+ if project && project.start_with?('/')
215
+ paths = NA.resolve_item_path(path: project, file: target)
216
+ if paths.count > 1
217
+ choices = paths.map { |p| "#{File.basename(target)}: #{p}" }
218
+ res = NA.choose_from(choices, prompt: 'Select target project', multiple: false)
219
+ if res
220
+ m = res.match(/: (.+)\z/)
221
+ project = m ? m[1] : project
222
+ else
223
+ NA.notify("#{NA.theme[:error]}No project selected, cancelled", exit_code: 1)
224
+ end
225
+ elsif paths.count == 1
226
+ project = paths.first
227
+ end
228
+ end
229
+
230
+ NA.add_action(target, project, action, note,
213
231
  finish: options[:finish], append: append,
214
232
  started_at: started_at, done_at: done_at, duration_seconds: duration_seconds)
215
233
  end
data/bin/commands/find.rb CHANGED
@@ -99,6 +99,38 @@ class App
99
99
  options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
100
100
  end
101
101
 
102
+ # Detect TaskPaper-style @search() syntax in arguments and delegate
103
+ # directly to the TaskPaper search runner (supports OR and extended
104
+ # TaskPaper syntax).
105
+ joined_args = args.join(' ')
106
+ if joined_args =~ /@search\((.+)\)/
107
+ inner = Regexp.last_match(1)
108
+ expr = "@search(#{inner})"
109
+ NA.run_taskpaper_search(
110
+ expr,
111
+ file: nil,
112
+ options: {
113
+ depth: depth,
114
+ notes: options[:notes],
115
+ nest: options[:nest],
116
+ omnifocus: options[:omnifocus],
117
+ no_file: options[:no_file],
118
+ times: options[:times],
119
+ human: options[:human],
120
+ search_notes: options[:search_notes],
121
+ invert: options[:invert],
122
+ regex: options[:regex],
123
+ project: options[:project],
124
+ done: options[:done],
125
+ require_na: false
126
+ }
127
+ )
128
+ next
129
+ end
130
+
131
+ tokens = nil
132
+ tags = []
133
+
102
134
  if options[:exact] || options[:regex]
103
135
  search = args.join(" ")
104
136
  else
@@ -122,24 +154,25 @@ class App
122
154
  search = search.gsub(/ +/, " ").strip
123
155
 
124
156
  all_req = options[:tagged].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
125
- tags = []
157
+ options_tags = []
126
158
  options[:tagged].join(",").split(/ *, */).each do |arg|
127
159
  m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
128
160
 
129
- tags.push({
130
- tag: m["tag"].wildcard_to_rx,
131
- comp: m["op"],
132
- value: m["val"],
133
- required: all_req || (!m["req"].nil? && m["req"] == "+"),
134
- negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
135
- })
161
+ options_tags.push({
162
+ tag: m["tag"].wildcard_to_rx,
163
+ comp: m["op"],
164
+ value: m["val"],
165
+ required: all_req || (!m["req"].nil? && m["req"] == "+"),
166
+ negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
167
+ })
136
168
  end
137
169
 
138
170
  search_for_done = false
139
- tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
171
+ options_tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
140
172
  options[:done] = true if search_for_done
141
173
 
142
- tokens = nil
174
+ tags = options_tags
175
+
143
176
  if options[:exact]
144
177
  tokens = search
145
178
  elsif options[:regex]
@@ -172,6 +205,13 @@ class App
172
205
  end
173
206
  end
174
207
 
208
+ # Support TaskPaper-style item paths in --project when value starts with '/'
209
+ project_filter_paths = nil
210
+ if options[:project]&.start_with?('/')
211
+ project_filter_paths = NA.resolve_item_path(path: options[:project], depth: depth)
212
+ options[:project] = nil
213
+ end
214
+
175
215
  todo = NA::Todo.new({
176
216
  depth: depth,
177
217
  done: options[:done],
@@ -185,6 +225,17 @@ class App
185
225
  require_na: false,
186
226
  })
187
227
 
228
+ # Apply item-path project filters, if any
229
+ if project_filter_paths && project_filter_paths.any?
230
+ todo.actions.delete_if do |a|
231
+ parents = Array(a.parent)
232
+ path = parents.join(':')
233
+ project_filter_paths.none? do |p|
234
+ path =~ /\A#{Regexp.escape(p)}(?::|\z)/i
235
+ end
236
+ end
237
+ end
238
+
188
239
  regexes = if tokens.is_a?(Array)
189
240
  tokens.delete_if { |token| token[:negate] }.map { |token| token[:token].wildcard_to_rx }
190
241
  else