howzit 2.1.24 → 2.1.25

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: 9539bebf0157985ac2d5a035923c1e7f78e942961f79a0729d20920d78c38f3a
4
- data.tar.gz: 0f16b31308c0e78f7d13e140363d2f62cd8da38658c5d1a9e29721ea0e6dda5f
3
+ metadata.gz: 330829109ccbd73e32ce82690327d2776c1a2f4e60004165fbcde80bdb2b68d6
4
+ data.tar.gz: 402805ec02154f4ed0f219bc8975be8b1f24e5ca8726e2eb6040f905f32854c7
5
5
  SHA512:
6
- metadata.gz: bb8ddc83529c66ddbcc1a71be7bdcdbf118427f08a206d7bc5a02e0b5b1a363d01f321e8865eab8f44a8b871dd8a8134e20e4d1bbd3a880cbbdc7c87f688062c
7
- data.tar.gz: 3a8501d2c2c7b7a35ac82701353b8b3162eb6b6b5890c98c5b171eb8be56bdaf9fc8cd841ef8610ca95482c2fe820964aae98c0945be8eaae6e2a1eb0b3432de
6
+ metadata.gz: 6f6f3ad81b25dc520f43a4ba1ca063a093dece43129c5ba86ed4364ecc4de5d9222f86ba64dbc91220d73197586846d92ff7f7b98251739881a3d140f453c0ea
7
+ data.tar.gz: 7b5eef5eb1cca9c57e3f7fa7af222911d43790b15bfb30b682c1c6f521cd1c54f8995a357141f1533830c888b91d79b107f3ec295b6238360719e1a6f2bb6032
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ### 2.1.25
2
+
3
+ 2025-12-19 07:41
4
+
5
+ #### NEW
6
+
7
+ - Added default metadata support to automatically run topics when executing `howzit --run` with no arguments. Supports multiple comma-separated topics with optional bracketed arguments (e.g., `default: Build Project, Run Tests[verbose]`).
8
+
9
+ #### FIXED
10
+
11
+ - Task titles are now correctly displayed in run reports and "Running" messages instead of showing the command. When a title is provided after @run or @include directives (e.g., `@run(ls) List directory`), the title is used throughout.
12
+ - Fixed color code interpretation issues when task or topic names contain dollar signs or braces. These characters are now properly escaped to prevent them from being interpreted as color template codes.
13
+ - Fixed issue where task titles containing braces would show literal `{x}` in output due to color template parsing.
14
+
1
15
  ### 2.1.24
2
16
 
3
17
  2025-12-13 07:11
@@ -955,18 +955,32 @@ module Howzit
955
955
  titles.each { |title| topic_matches.push(find_topic(title)[0]) }
956
956
  process_topic_matches(topic_matches, output)
957
957
  elsif !Howzit.cli_args.empty?
958
- # Collect all topic matches first (showing menus as needed)
959
- search = topic_search_terms_from_cli
960
- topic_matches = collect_topic_matches(search, output)
961
- process_topic_matches(topic_matches, output)
958
+ # Check if first arg is "default"
959
+ if Howzit.options[:run] && Howzit.cli_args[0].downcase == 'default'
960
+ process_default_metadata(output)
961
+ else
962
+ # Collect all topic matches first (showing menus as needed)
963
+ search = topic_search_terms_from_cli
964
+ topic_matches = collect_topic_matches(search, output)
965
+ process_topic_matches(topic_matches, output)
966
+ end
962
967
  else
963
- # No arguments - show all topics
968
+ # No arguments
964
969
  if Howzit.options[:run]
965
- Howzit.run_log = []
966
- Howzit.multi_topic_run = topics.length > 1
970
+ # Check for default metadata when running with no args
971
+ if @metadata.key?('default')
972
+ process_default_metadata(output)
973
+ else
974
+ Howzit.run_log = []
975
+ Howzit.multi_topic_run = topics.length > 1
976
+ topics.each { |k| output.push(process_topic(k, false, single: false)) }
977
+ finalize_output(output)
978
+ end
979
+ else
980
+ # Show all topics
981
+ topics.each { |k| output.push(process_topic(k, false, single: false)) }
982
+ finalize_output(output)
967
983
  end
968
- topics.each { |k| output.push(process_topic(k, false, single: false)) }
969
- finalize_output(output)
970
984
  end
971
985
  end
972
986
 
@@ -1060,6 +1074,77 @@ module Howzit
1060
1074
  finalize_output(output)
1061
1075
  end
1062
1076
 
1077
+ ##
1078
+ ## Process default metadata and run the specified topics
1079
+ ##
1080
+ ## @param output [Array] Output array
1081
+ ##
1082
+ def process_default_metadata(output)
1083
+ default_value = @metadata['default']
1084
+ return if default_value.nil? || default_value.strip.empty?
1085
+
1086
+ Howzit.run_log = []
1087
+ default_topics = parse_default_metadata(default_value)
1088
+ Howzit.multi_topic_run = default_topics.length > 1
1089
+
1090
+ topic_specs = []
1091
+ default_topics.each do |topic_spec|
1092
+ topic_name, args = parse_topic_with_args(topic_spec)
1093
+ matches = find_topic(topic_name)
1094
+ if matches.empty?
1095
+ output.push(%({bR}ERROR:{xr} No topic match found for {bw}#{topic_name}{x}\n).c)
1096
+ else
1097
+ # Store topic and its arguments together (take first match, like @include does)
1098
+ topic_specs << [matches[0], args]
1099
+ end
1100
+ end
1101
+
1102
+ if topic_specs.empty?
1103
+ Util.show(output.join("\n"), { color: true, highlight: false, paginate: false, wrap: 0 })
1104
+ Process.exit 1
1105
+ end
1106
+
1107
+ # Run each topic with its specific arguments
1108
+ topic_specs.each do |topic_match, args|
1109
+ # Set arguments if provided, otherwise clear them
1110
+ if args && !args.empty?
1111
+ Howzit.arguments = args.split(/ *, */).map(&:render_arguments)
1112
+ else
1113
+ Howzit.arguments = []
1114
+ end
1115
+ output.push(process_topic(topic_match, Howzit.options[:run], single: true))
1116
+ end
1117
+ finalize_output(output)
1118
+ end
1119
+
1120
+ ##
1121
+ ## Parse default metadata value into individual topic specifications
1122
+ ##
1123
+ ## @param default_value [String] The default metadata value
1124
+ ##
1125
+ ## @return [Array] Array of topic specification strings
1126
+ ##
1127
+ def parse_default_metadata(default_value)
1128
+ default_value.strip.split(/\s*,\s*/).map(&:strip).reject(&:empty?)
1129
+ end
1130
+
1131
+ ##
1132
+ ## Parse a topic specification that may include bracketed arguments
1133
+ ##
1134
+ ## @param topic_spec [String] Topic specification like "Run Snippet[document.md]"
1135
+ ##
1136
+ ## @return [Array] [topic_name, args_string]
1137
+ ##
1138
+ def parse_topic_with_args(topic_spec)
1139
+ if topic_spec =~ /\[(.*?)\]$/
1140
+ args = Regexp.last_match(1)
1141
+ topic_name = topic_spec.sub(/\[.*?\]$/, '').strip
1142
+ [topic_name, args]
1143
+ else
1144
+ [topic_spec.strip, nil]
1145
+ end
1146
+ end
1147
+
1063
1148
  ##
1064
1149
  ## Finalize and display output with run summary if applicable
1065
1150
  ##
data/lib/howzit/colors.rb CHANGED
@@ -272,7 +272,7 @@ module Howzit
272
272
  def template(input)
273
273
  input = input.join(' ') if input.is_a? Array
274
274
  fmt = input.gsub(/%/, '%%')
275
- fmt = fmt.gsub(/(?<!\\u|\$)\{(\w+)\}/i) do
275
+ fmt = fmt.gsub(/(?<!\\u|\$|\\\\)\{(\w+)\}/i) do
276
276
  m = Regexp.last_match(1)
277
277
  if m =~ /^[wkglycmrWKGLYCMRdbuix]+$/
278
278
  m.split('').map { |c| "%<#{c}>s" }.join('')
@@ -287,7 +287,9 @@ module Howzit
287
287
  Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred,
288
288
  d: dark, b: bold, u: underline, i: italic, x: reset }
289
289
 
290
- fmt.empty? ? input : format(fmt, colors)
290
+ result = fmt.empty? ? input : format(fmt, colors)
291
+ # Unescape braces that were escaped to prevent color code interpretation
292
+ result.gsub(/\\\{/, '{').gsub(/\\\}/, '}')
291
293
  end
292
294
  end
293
295
 
@@ -29,8 +29,14 @@ module Howzit
29
29
  def format_line(entry, prefix_topic)
30
30
  symbol = entry[:success] ? '✅' : '❌'
31
31
  parts = ["#{symbol} "]
32
- parts << "{bw}#{entry[:topic]}{x}: " if prefix_topic && entry[:topic] && !entry[:topic].empty?
33
- parts << "{by}#{entry[:task]}{x}"
32
+ if prefix_topic && entry[:topic] && !entry[:topic].empty?
33
+ # Escape braces in topic name to prevent color code interpretation
34
+ topic_escaped = entry[:topic].gsub(/\{/, '\\{').gsub(/\}/, '\\}')
35
+ parts << "{bw}#{topic_escaped}{x}: "
36
+ end
37
+ # Escape braces in task name to prevent color code interpretation
38
+ task_escaped = entry[:task].gsub(/\{/, '\\{').gsub(/\}/, '\\}')
39
+ parts << "{by}#{task_escaped}{x}"
34
40
  unless entry[:success]
35
41
  reason = entry[:exit_status] ? "exit code #{entry[:exit_status]}" : 'failed'
36
42
  parts << " {br}(#{reason}){x}"
@@ -61,7 +67,7 @@ module Howzit
61
67
  table_lines.join("\n")
62
68
  end
63
69
 
64
- def table_row_colored(status, task, task_plain, status_width, task_width)
70
+ def table_row_colored(status, task, task_plain, _status_width, task_width)
65
71
  task_padding = task_width - task_plain.length
66
72
 
67
73
  "| #{status} | #{task}#{' ' * task_padding} |"
@@ -76,11 +82,15 @@ module Howzit
76
82
  task_parts_plain = []
77
83
 
78
84
  if prefix_topic && entry[:topic] && !entry[:topic].empty?
79
- task_parts << "{bw}#{entry[:topic]}{x}: "
85
+ # Escape braces in topic name to prevent color code interpretation
86
+ topic_escaped = entry[:topic].gsub(/\{/, '\\{').gsub(/\}/, '\\}')
87
+ task_parts << "{bw}#{topic_escaped}{x}: "
80
88
  task_parts_plain << "#{entry[:topic]}: "
81
89
  end
82
90
 
83
- task_parts << "{by}#{entry[:task]}{x}"
91
+ # Escape braces in task name to prevent color code interpretation
92
+ task_escaped = entry[:task].gsub(/\{/, '\\{').gsub(/\}/, '\\}')
93
+ task_parts << "{by}#{task_escaped}{x}"
84
94
  task_parts_plain << entry[:task]
85
95
 
86
96
  unless entry[:success]
data/lib/howzit/task.rb CHANGED
@@ -26,7 +26,7 @@ module Howzit
26
26
  @arguments = attributes[:arguments] || []
27
27
 
28
28
  @type = attributes[:type] || :run
29
- @title = attributes[:title] || nil
29
+ @title = attributes[:title].nil? ? nil : attributes[:title].to_s
30
30
  @parent = attributes[:parent] || nil
31
31
 
32
32
  @action = attributes[:action].render_arguments || nil
@@ -98,8 +98,19 @@ module Howzit
98
98
  ## Execute a run task
99
99
  ##
100
100
  def run_run
101
- title = Howzit.options[:show_all_code] ? @action : @title
102
- Howzit.console.info("#{@prefix}{bg}Running {bw}#{title}{x}".c)
101
+ # If a title was explicitly provided (different from action), always use it
102
+ # Otherwise, use action (or respect show_all_code if no title)
103
+ display_title = if @title && !@title.empty? && @title != @action
104
+ # Title was explicitly provided, use it
105
+ @title
106
+ elsif Howzit.options[:show_all_code]
107
+ # No explicit title, show code if requested
108
+ @action
109
+ else
110
+ # No explicit title, use title if available (might be same as action), otherwise action
111
+ @title && !@title.empty? ? @title : @action
112
+ end
113
+ Howzit.console.info("#{@prefix}{bg}Running {bw}#{display_title}{x}".c)
103
114
  ENV['HOWZIT_SCRIPTS'] = File.expand_path('~/.config/howzit/scripts')
104
115
  res = system(@action)
105
116
  update_last_status(res ? 0 : 1)
@@ -110,8 +121,19 @@ module Howzit
110
121
  ## Execute a copy task
111
122
  ##
112
123
  def run_copy
113
- title = Howzit.options[:show_all_code] ? @action : @title
114
- Howzit.console.info("#{@prefix}{bg}Copied {bw}#{title}{bg} to clipboard{x}".c)
124
+ # If a title was explicitly provided (different from action), always use it
125
+ # Otherwise, use action (or respect show_all_code if no title)
126
+ display_title = if @title && !@title.empty? && @title != @action
127
+ # Title was explicitly provided, use it
128
+ @title
129
+ elsif Howzit.options[:show_all_code]
130
+ # No explicit title, show code if requested
131
+ @action
132
+ else
133
+ # No explicit title, use title if available (might be same as action), otherwise action
134
+ @title && !@title.empty? ? @title : @action
135
+ end
136
+ Howzit.console.info("#{@prefix}{bg}Copied {bw}#{display_title}{bg} to clipboard{x}".c)
115
137
  Util.os_copy(@action)
116
138
  @last_status = 0
117
139
  true
data/lib/howzit/topic.rb CHANGED
@@ -288,11 +288,18 @@ module Howzit
288
288
  def define_task_args(keys)
289
289
  cmd = keys[:cmd]
290
290
  obj = keys[:action]
291
- title = keys[:title].nil? ? obj : keys[:title].strip
292
- title = Howzit.options[:show_all_code] ? obj : title
291
+ # Extract and clean the title
292
+ raw_title = keys[:title]
293
+ # Determine the title: use provided title if available, otherwise use action
294
+ title = if raw_title.nil? || raw_title.to_s.strip.empty?
295
+ obj
296
+ else
297
+ raw_title.to_s.strip
298
+ end
299
+ # Store the actual title (not overridden by show_all_code - that's only for display)
293
300
  task_args = { type: :include,
294
301
  arguments: nil,
295
- title: title,
302
+ title: title.dup, # Make a copy to avoid reference issues
296
303
  action: obj,
297
304
  parent: self }
298
305
  case cmd
@@ -303,6 +310,7 @@ module Howzit
303
310
  Howzit.arguments = args
304
311
  arguments
305
312
  title.sub!(/ *\[.*?\] *$/, '')
313
+ task_args[:title] = title
306
314
  end
307
315
 
308
316
  task_args[:type] = :include
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.1.24'
6
+ VERSION = '2.1.25'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.24
4
+ version: 2.1.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra