na 1.2.98 → 1.2.101

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: 0f82dc36deae233300843fac7ccd81408a28e4f8c29266ef036df11c731ef94a
4
- data.tar.gz: 544b783521ff49f4e169fc7b4fb0be1523264217503d1685ef80d2aa2750a8fb
3
+ metadata.gz: e1cf2028aa8b5420b1943873ccd9defc0cea44ba83ed76d437d33fd2676d753d
4
+ data.tar.gz: a33b579fa622f70a4b3771dc86a63d324be2fd751aa87980c02ad1a6f1556221
5
5
  SHA512:
6
- metadata.gz: 5abe2d76e969c58010a3d566074c3fc7d80817657f6f59c3a2039f5f0fe53028dec707be05db546642644b85ac9043006e8d86d0dbb2189465943af60442421d
7
- data.tar.gz: 670c1efea7d6365266128bac5cc184001c3998056300cbb07fcde1a8b4cda8e9f1bf5e686acc14840b7fde9efcdea74b18c68fdf3dad334cdd0dbd393177d0cc
6
+ metadata.gz: b14815f0edac0f8d48ba15d0005e2c57b10e1db28e6fba687374d86b17cfd087eed8476a5e816d5ffb425009bfd20ee2b852a0fc5be5430efecc81d34843ae86
7
+ data.tar.gz: ea4ef1b69c4bac50a371362439de89b3721d07b42533cc1bcff9e377ead25b8034a25f2ab9c98e1889c6ea5beec636670d469a9388b4178ea5b79296942c6790
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-02-24 10:37:38 UTC using RuboCop version 1.81.7.
3
+ # on 2026-02-27 15:51:35 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
@@ -24,7 +24,7 @@ Lint/UnusedMethodArgument:
24
24
  # Offense count: 58
25
25
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
26
26
  Metrics/AbcSize:
27
- Max: 309
27
+ Max: 326
28
28
 
29
29
  # Offense count: 14
30
30
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
@@ -40,12 +40,12 @@ Metrics/BlockNesting:
40
40
  # Offense count: 6
41
41
  # Configuration parameters: CountComments, CountAsOne.
42
42
  Metrics/ClassLength:
43
- Max: 1495
43
+ Max: 1502
44
44
 
45
45
  # Offense count: 41
46
46
  # Configuration parameters: AllowedMethods, AllowedPatterns.
47
47
  Metrics/CyclomaticComplexity:
48
- Max: 91
48
+ Max: 97
49
49
 
50
50
  # Offense count: 63
51
51
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
@@ -55,7 +55,7 @@ Metrics/MethodLength:
55
55
  # Offense count: 5
56
56
  # Configuration parameters: CountComments, CountAsOne.
57
57
  Metrics/ModuleLength:
58
- Max: 1497
58
+ Max: 1504
59
59
 
60
60
  # Offense count: 5
61
61
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
@@ -65,7 +65,7 @@ Metrics/ParameterLists:
65
65
  # Offense count: 39
66
66
  # Configuration parameters: AllowedMethods, AllowedPatterns.
67
67
  Metrics/PerceivedComplexity:
68
- Max: 104
68
+ Max: 110
69
69
 
70
70
  # Offense count: 1
71
71
  # Configuration parameters: ForbiddenDelimiters.
@@ -140,7 +140,7 @@ Style/YAMLFileRead:
140
140
  Exclude:
141
141
  - 'lib/na/theme.rb'
142
142
 
143
- # Offense count: 38
143
+ # Offense count: 40
144
144
  # This cop supports safe autocorrection (--autocorrect).
145
145
  # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
146
146
  # URISchemes: http, https
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ### 1.2.101
2
+
3
+ 2026-04-26 12:06
4
+
5
+ #### CHANGED
6
+
7
+ - Allow next --available with filters to match actions without the default @na tag.
8
+ - Bump the gem version to 1.2.101.
9
+
10
+ #### NEW
11
+
12
+ - Add next --available/-a to show the first available action per project.
13
+
14
+ #### FIXED
15
+
16
+ - Match unique nested projects by leaf name in add/update
17
+ - Make --color force colored output even when stdout is not a TTY.
18
+
19
+ ### 1.2.100
20
+
21
+ 2026-02-27 09:53
22
+
23
+ #### FIXED
24
+
25
+ - Match unique nested projects by leaf name in add/update
26
+
1
27
  ### 1.2.98
2
28
 
3
29
  2026-02-24 11:26
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.98)
4
+ na (1.2.101)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  csv (~> 3.2)
7
7
  git (~> 3.0.0)
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.98.
12
+ The current version of `na` is 1.2.101.
13
13
 
14
14
 
15
15
  ### Table of contents
@@ -257,7 +257,7 @@ SYNOPSIS
257
257
  na [global options] command [command options] [arguments...]
258
258
 
259
259
  VERSION
260
- 1.2.98
260
+ 1.2.101
261
261
 
262
262
  GLOBAL OPTIONS
263
263
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -522,6 +522,7 @@ DESCRIPTION
522
522
  Next actions are actions which contain the next action tag (default @na), do not contain @done, and are not in the Archive project. Arguments will target a todo file from history, whether it's in the current directory or not. Todo file queries can include path components separated by / or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).
523
523
 
524
524
  COMMAND OPTIONS
525
+ -a, --available - Show only the first available action per project
525
526
  --all - Show next actions from all known todo files (in any directory)
526
527
  -d, --depth=DEPTH - Recurse to depth (default: none)
527
528
  --divider=STRING - Divider string for text IO (default: none)
data/bin/commands/next.rb CHANGED
@@ -25,6 +25,9 @@ class App
25
25
  c.desc "Show next actions from all known todo files (in any directory)"
26
26
  c.switch %i[all], negatable: false, default_value: false
27
27
 
28
+ c.desc "Show only the first available action per project"
29
+ c.switch %i[a available], negatable: false
30
+
28
31
  c.desc "Display matches from a known todo file anywhere in history (short name)"
29
32
  c.arg_name "TODO"
30
33
  c.flag %i[in todo], multiple: true
@@ -169,6 +172,8 @@ class App
169
172
  next
170
173
  end
171
174
 
175
+ available = options[:available]
176
+
172
177
  if options[:exact] || options[:regex]
173
178
  search = options[:search].join(" ")
174
179
  else
@@ -288,9 +293,22 @@ class App
288
293
  end
289
294
 
290
295
  NA.na_tag = options[:tag] unless options[:tag].nil?
291
- require_na = true
292
296
 
293
- tag = [{ tag: NA.na_tag, value: nil, required: true, negate: false }]
297
+ # When -a/--available is combined with --all or any filtering flags,
298
+ # relax the implicit NA_TAG requirement and show the first available
299
+ # action that matches the filters instead.
300
+ filtered_mode = options[:all] ||
301
+ options[:save] ||
302
+ !options[:tag].nil? ||
303
+ options[:project] ||
304
+ options[:tagged].any? ||
305
+ options[:priority].any? ||
306
+ (options[:search].respond_to?(:any?) && options[:search].any?)
307
+
308
+ require_na = !(available && filtered_mode)
309
+
310
+ tag = []
311
+ tag << { tag: NA.na_tag, value: nil, required: true, negate: false } if require_na
294
312
  tag << { tag: "done", value: nil, negate: true } unless options[:done]
295
313
  tag.concat(tags)
296
314
 
@@ -378,17 +396,24 @@ class App
378
396
  end
379
397
  end
380
398
  end
381
- todo.actions.output(depth,
382
- { files: todo.files,
383
- nest: options[:nest],
384
- nest_projects: options[:omnifocus],
385
- notes: options[:notes],
386
- no_files: options[:no_file],
387
- times: options[:times],
388
- human: options[:human],
389
- only_timed: options[:only_timed],
390
- json_times: options[:json_times],
391
- only_times: options[:only_times] })
399
+
400
+ actions = if available
401
+ todo.actions.first_available_per_project(require_na: require_na)
402
+ else
403
+ todo.actions
404
+ end
405
+
406
+ actions.output(depth,
407
+ { files: todo.files,
408
+ nest: options[:nest],
409
+ nest_projects: options[:omnifocus],
410
+ notes: options[:notes],
411
+ no_files: options[:no_file],
412
+ times: options[:times],
413
+ human: options[:human],
414
+ only_timed: options[:only_timed],
415
+ json_times: options[:json_times],
416
+ only_times: options[:only_times] })
392
417
  end
393
418
  end
394
419
  end
data/bin/na CHANGED
@@ -116,7 +116,7 @@ class App
116
116
  NA::Plugins.ensure_plugins_home
117
117
  NA.verbose = global[:debug]
118
118
  NA::Pager.paginate = global[:pager] && $stdout.isatty
119
- NA::Color.coloring = global[:color] && $stdout.isatty
119
+ NA::Color.coloring = global[:color] && ($stdout.isatty || NA.globals.include?('--color'))
120
120
  NA.extension = global[:ext]
121
121
  NA.include_ext = global[:include_ext]
122
122
  NA.na_tag = global[:na_tag]
data/lib/na/actions.rb CHANGED
@@ -7,6 +7,30 @@ module NA
7
7
  super
8
8
  end
9
9
 
10
+ # Return the first available action per project path.
11
+ #
12
+ # @param require_na [Boolean] If true, only consider actions with the NA tag
13
+ # @return [NA::Actions] A new Actions collection with at most one action per project
14
+ def first_available_per_project(require_na: true)
15
+ NA::Benchmark.measure('Actions.first_available_per_project') do
16
+ seen = {}
17
+ selected = NA::Actions.new
18
+
19
+ each do |action|
20
+ next if require_na && !action.respond_to?(:na?) ? false : (require_na && !action.na?)
21
+
22
+ project_path = Array(action.parent).join(':')
23
+ next if project_path.empty?
24
+ next if seen.key?(project_path)
25
+
26
+ seen[project_path] = true
27
+ selected << action
28
+ end
29
+
30
+ selected
31
+ end
32
+ end
33
+
10
34
  # Pretty print a list of actions
11
35
  #
12
36
  # @param depth [Integer] The depth of the action
@@ -502,7 +502,17 @@ module NA
502
502
  # If move is set, update add.parent to the target project
503
503
  add.parent = target_proj.project.split(':') if move && target_proj
504
504
  project_path = add.parent.join(':')
505
- target_proj ||= projects.select { |proj| proj.project =~ /^#{project_path}$/i }.first
505
+ target_proj ||= projects.select { |proj| proj.project =~ /^#{Regexp.escape(project_path)}$/i }.first
506
+
507
+ if target_proj.nil? && !project_path.empty? && !project_path.include?(':')
508
+ # Fallback: if a bare leaf name was provided and it uniquely matches
509
+ # the final segment of an existing nested project, use that project
510
+ candidates = projects.select do |proj|
511
+ leaf = proj.project.to_s.split(':').last
512
+ leaf&.casecmp?(project_path)
513
+ end
514
+ target_proj = candidates.first if candidates.size == 1
515
+ end
506
516
 
507
517
  if target_proj.nil? && !project_path.empty?
508
518
  display_path = project_path.tr(':', '/')
@@ -515,7 +525,7 @@ module NA
515
525
  created_proj = insert_project(target, project_path)
516
526
  contents = target.read_file.split("\n")
517
527
  projects = find_projects(target)
518
- target_proj = projects.select { |proj| proj.project =~ /^#{project_path}$/i }.first || created_proj
528
+ target_proj = projects.select { |proj| proj.project =~ /^#{Regexp.escape(project_path)}$/i }.first || created_proj
519
529
  end
520
530
 
521
531
  add.parent = target_proj.project.split(':') if target_proj
data/lib/na/version.rb CHANGED
@@ -5,5 +5,5 @@
5
5
  module Na
6
6
  ##
7
7
  # Current version of the na gem.
8
- VERSION = '1.2.98'
8
+ VERSION = '1.2.101'
9
9
  end
data/src/_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 <!--VER-->1.2.97<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.2.100<!--END VER-->.
13
13
 
14
14
  <!--GITHUB-->
15
15
  ### Table of contents
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: na
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.98
4
+ version: 1.2.101
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -360,7 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
360
360
  - !ruby/object:Gem::Version
361
361
  version: '0'
362
362
  requirements: []
363
- rubygems_version: 4.0.3
363
+ rubygems_version: 3.6.7
364
364
  specification_version: 4
365
365
  summary: A command line tool for adding and listing project todos
366
366
  test_files: []