timet 1.0.0 → 1.2.0

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: 2f05df826f56c4161b878136fed6db433c86f717d1853dadc5b0255f98c5197e
4
- data.tar.gz: 44699896bbfcc7b49409f4cb2bece028b4719ef9b1e97f7d83221ea1073e863f
3
+ metadata.gz: a3f1c37d739d4c6712cf21af39357a45ded45b8a63d8d7e9932ac859fe72b000
4
+ data.tar.gz: 20bf38e7554d3eb1326d1d7e2ae1095792c9b20a473d383f9f123e2ac0bbd2b2
5
5
  SHA512:
6
- metadata.gz: 4af0f240762026b43a3e4c9f3833d81e51729c0ea6916537be9a9893e40d602e7f7fab49d2d552a6ee736f3efd3e8c7ea6f8ad2ff01f2306c97a0e6568b354ef
7
- data.tar.gz: d368ea5f22cabd607a30056cbca07b4bc80501775a1bea9094d7c842a8277f3d2656fe334a6a038b2671475b9278c00ea7eb9adec15190e1f35e5f1dec44f142
6
+ metadata.gz: 92892c05063d444a713eaafec9eb63ff970a4926364b594dd31b61f229e9b415d3a171da61ffaa96660512708076423348ece7f120c0e21e075aa777e8e5b1b4
7
+ data.tar.gz: 3e1799d361eb28b500ce81740eb9ee1f16d55048e9425203aa6482265a9bc1d891e6d6897c626b454b2a18854523b99ed4acb0edf3a63e0dc043f16d1ebf4b04
data/CHANGELOG.md CHANGED
@@ -1,13 +1,73 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.2.0] - 2024-10-11
4
+
5
+ **Improvements:**
6
+
7
+ - Enhanced the README to provide more detailed and user-friendly information about the tool.
8
+ - Added a visually appealing title and logo to make the README more engaging.
9
+ - Organized key features into bullet points for better readability.
10
+ - Provided clear and concise installation instructions.
11
+ - Made the command reference more user-friendly by adding a table of contents.
12
+ - Added more details about data storage and development guidelines.
13
+ - Encouraged contributions and provided guidelines for contributing.
14
+ - Made the support section more prominent.
15
+ - Ensured the license and code of conduct are clearly stated.
16
+ - Updated the `timet` gem version to 1.2.0 in `Gemfile.lock`.
17
+ - Refactored the `play_sound_and_notify` condition to use the `positive?` method for better readability.
18
+ - Enhanced table header formatting with a blinking effect for better user interaction.
19
+ - Added new methods for tag distribution and time block chart visualization:
20
+ - `format_tag_distribution`: Displays tag distribution with progress bars.
21
+ - `print_time_block_chart`: Prints the entire time block chart.
22
+ - `print_header`: Prints the header of the time block chart.
23
+ - `print_blocks`: Prints the block characters for each hour.
24
+ - `get_block_char`: Determines the block character based on value.
25
+ - Added `count_seconds_per_hour_block` method to count seconds per hour block.
26
+ - Added `aggregate_hash_values` method to aggregate hash values.
27
+ - Integrated new methods into `time_report` to enhance time tracking visualization.
28
+ - Updated the version number in `lib/timet/version.rb` to 1.2.0.
29
+
30
+ ### Additional Considerations:
31
+
32
+ - The enhancements made in this pull request aim to improve the user experience and provide more powerful visualization and reporting capabilities for time tracking.
33
+ - Reviewers are encouraged to test the new visualization methods and provide feedback on their effectiveness and usability.
34
+ - The README updates should make it easier for new users to understand and use the `timet` tool.
35
+
36
+ ## [1.0.0] - 2024-10-07
37
+
38
+ **Improvements:**
39
+
40
+ - Added a `pomodoro` option to the `start` command to specify Pomodoro time in minutes.
41
+ - Updated the `start` method to accept an optional `pomodoro` parameter and call `play_sound_and_notify` if Pomodoro time is provided.
42
+ - Improved the `stop` method to accept an optional `display` parameter and conditionally call `summary`.
43
+ - Added `play_sound_and_notify` method to `application_helper.rb` for playing a sound and sending a notification after a specified time.
44
+ - Updated RSpec tests to reflect the new `pomodoro` parameter and `display` parameter in the `start` and `stop` methods, respectively.
45
+ - Converted Pomodoro time from minutes to seconds before passing it to `play_sound_and_notify`.
46
+
47
+ ### Bug fixes:
48
+
49
+ - Ensured Pomodoro time is a positive integer before invoking `play_sound_and_notify`.
50
+
51
+ #### Tasks:
52
+
53
+ - Update README.md to document the new Pomodoro feature.
54
+
55
+ ### Additional Considerations:
56
+
57
+ - The `pomodoro` option is designed to be flexible, allowing users to specify any duration in minutes for their Pomodoro sessions. This flexibility caters to users who may prefer different interval lengths based on their work habits and preferences.
58
+ - The `play_sound_and_notify` method is a new addition to the `application_helper.rb` file, providing a mechanism for notifying users when their Pomodoro session ends. This feature includes both a sound notification and a system notification to ensure users are aware of the end of their work interval.
59
+ - The `stop` method has been improved to accept an optional `display` parameter, which allows users to conditionally call the `summary` method. This enhancement provides more control over when the summary of the time tracking session is displayed.
60
+ - The RSpec tests have been updated to reflect the new parameters and functionality introduced in this pull request, ensuring that the code remains robust and reliable.
61
+
3
62
  ## [0.9.2] - 2024-10-06
4
63
 
5
64
  **Improvements:**
65
+
6
66
  - Improved the description of the 'start' command to clarify the usage of optional notes.
7
67
 
8
68
  **Bug fixes:**
9
- - Modified the 'display_item' method to handle cases where 'updated_item' is nil, ensuring that the original 'item' is displayed instead.
10
69
 
70
+ - Modified the 'display_item' method to handle cases where 'updated_item' is nil, ensuring that the original 'item' is displayed instead.
11
71
 
12
72
  ## [0.9.1] - 2024-10-04
13
73
 
data/README.md CHANGED
@@ -5,18 +5,42 @@
5
5
 
6
6
  # Timet
7
7
 
8
+ ![Timet](timet.webp)
9
+
8
10
  Timet refers to a command-line tool designed to track your activities by recording the time spent on each task, allowing you to monitor your work hours and productivity directly from your terminal without needing a graphical interface; essentially, it's a way to log your time spent on different projects or tasks using simple text commands
9
11
 
10
- Timet utilizes SQLite to store your time tracking data. This means your data is stored locally and securely, with no need for external databases or cloud storage. This makes Timet lightweight, fast, and perfect for users who value privacy and control over their data.
12
+ **Key Features:**
11
13
 
12
- While a YAML file might seem like a simple option for storing time tracking data, Timet leverages SQLite for several key advantages:
14
+ - **Local Data Storage:** Timet utilizes SQLite to store your time tracking data locally, ensuring privacy and security.
15
+ - **Lightweight and Fast:** Its efficient design and local data storage make Timet a speedy and responsive tool.
16
+ - **Structured Data:** SQLite ensures your data is organized and easily accessible.
17
+ - **Scalability:** Timet can handle growing time tracking needs.
18
+ - **Data Integrity:** SQLite maintains the accuracy and consistency of your data.
19
+ - **Querying and Reporting:** Generate detailed reports for specific periods.
20
+ - **CSV Export:** Easily export your time tracking data to CSV format for further analysis or sharing.
21
+ - **Pomodoro Integration:** The pomodoro option in the start command enhances time tracking by integrating the Pomodoro Technique.
22
+ - **Block Time Plot:** Visualizes the distribution of tracked time across a 24-hour period, with bars in each column representing the amount of time tracked during that specific hour.
23
+ - **Tag Distribution Plot:** Illustrates the proportion of total tracked time allocated to each tag, showing the relative contribution of each tag to the overall time tracked.
13
24
 
14
- - Structured Data
15
- - Scalability
16
- - Data Integrity
17
- - Querying and Reporting
25
+ Example:
18
26
 
19
- In addition, if possible, export your time tracking data to CSV for analysis and sharing.
27
+ ```bash
28
+ Tracked time report [today]:
29
+ +-------+------------+--------+----------+----------+----------+--------------------------+
30
+ | Id | Date | Tag | Start | End | Duration | Notes |
31
+ +-------+------------+--------+----------+----------+----------+--------------------------+
32
+ | 20 | 2024-10-10 | Tag8 | 19:26:58 | 20:26:58 | 01:00:00 | Notes 2 |
33
+ | 19 | | Tag3 | 07:52:26 | 08:52:26 | 01:00:00 | Notes 7 |
34
+ +-------+------------+--------+----------+----------+----------+--------------------------+
35
+ | Total: | 02:00:00 | |
36
+ +-------+------------+--------+----------+----------+----------+--------------------------+
37
+
38
+ ⏳ ↦ ┏ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
39
+ ┗ ▂▂ ▇▇ ▅▅ ▄▄
40
+
41
+ Tag8: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
42
+ Tag3: 50.0% ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅
43
+ ```
20
44
 
21
45
  ## Requirements
22
46
 
@@ -39,14 +63,20 @@ gem install timet
39
63
 
40
64
  ## Usage
41
65
 
42
- - timet start [tag] --notes="" --pomodoro=[minutes]**: Starts tracking time for a task labeled with the provided [tag], notes and "pomodoro time" in minutes (optional). Example:
66
+ ### Command Aliases
67
+
68
+ - `timet`: The primary command for interacting with the Timet application.
69
+ - `tt`: An alias for the `timet` command, providing a shorter alternative.
70
+
71
+ ---
72
+ - **timet start [tag] --notes="" --pomodoro=[minutes]**: Starts tracking time for a task labeled with the provided [tag], notes and "pomodoro time" in minutes (optional). Example:
43
73
 
44
74
  ```bash
45
75
  timet start task1 --notes="Meeting with client" --pomodoro=25
46
76
 
47
77
  or
48
78
 
49
- timet start task1 "Meeting with client" 25
79
+ tt start task1 "Meeting with client" 25
50
80
  ```
51
81
 
52
82
  ```
@@ -110,7 +140,7 @@ gem install timet
110
140
  - **Interactive Mode:**
111
141
 
112
142
  ```bash
113
- timet e 1
143
+ timet edit 1
114
144
  ```
115
145
 
116
146
  ```
@@ -201,9 +231,14 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/frankv
201
231
 
202
232
  Many people have contacted me asking how to contribute. Any contribution, from a virtual coffee to a kind word, is greatly appreciated and helps me continue my work. Please only donate if you're able, as there are no refunds. Your support is entirely voluntary, and I thank you for your consideration.
203
233
 
234
+ **Bitcoin Address:**
235
+ ```sh
236
+ bc1qkg9me2jsuhpzu2hp9kkpxagwtf9ewnyfl4kszl
237
+ ```
238
+
204
239
  ![Buy me a coffee!](btc.png)
205
240
 
206
- bc1qkg9me2jsuhpzu2hp9kkpxagwtf9ewnyfl4kszl
241
+ ---
207
242
 
208
243
  ## License
209
244
 
data/bin/tt ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/timet'
5
+
6
+ Timet::Application.start(ARGV)
@@ -66,7 +66,7 @@ module Timet
66
66
 
67
67
  if VALID_STATUSES_FOR_INSERTION.include?(@db.last_item_status)
68
68
  @db.insert_item(start_time, tag, notes)
69
- play_sound_and_notify(pomodoro * 60, tag) if pomodoro > 0
69
+ play_sound_and_notify(pomodoro * 60, tag) if pomodoro.positive?
70
70
  end
71
71
  summary
72
72
  end
@@ -245,6 +245,18 @@ module Timet
245
245
  true
246
246
  end
247
247
 
248
+ # Displays the current version of the Timet gem.
249
+ #
250
+ # @example
251
+ # $ timet version
252
+ # 1.0.0
253
+ #
254
+ # @return [void] This method does not return a value; it prints the version to the standard output.
255
+ desc 'version', 'version'
256
+ def version
257
+ puts Timet::VERSION
258
+ end
259
+
248
260
  private
249
261
 
250
262
  # Deletes a tracking item from the database by its ID and prints a confirmation message.
@@ -14,7 +14,7 @@ module Timet
14
14
  # @note The method constructs a string representing the table header and prints it.
15
15
  def format_table_header
16
16
  header = <<~TABLE
17
- Tracked time report \u001b[31m[#{@filter}]\033[0m:
17
+ Tracked time report \e[5m\u001b[31m[#{@filter}]\033[0m:
18
18
  #{format_table_separator}
19
19
  \033[32m| Id | Date | Tag | Start | End | Duration | Notes |\033[0m
20
20
  #{format_table_separator}
@@ -64,5 +64,120 @@ module Timet
64
64
  notes = "#{notes.slice(0, 20)}..." if notes.length > 20
65
65
  notes.ljust(23)
66
66
  end
67
+
68
+ # @!method format_tag_distribution(duration_by_tag)
69
+ # Formats and displays the tag distribution.
70
+ #
71
+ # @example
72
+ # duration_by_tag = { "timet" => 3600, "nextjs" => 1800 }
73
+ # Formatter.format_tag_distribution(duration_by_tag)
74
+ # # Output:
75
+ # # timet: 66.67% \u001b[38;5;42m====================\u001b[0m
76
+ # # nextjs: 33.33% \u001b[38;5;42m==========\u001b[0m
77
+ #
78
+ # @param duration_by_tag [Hash<String, Integer>] A hash where keys are tags and values are durations in seconds.
79
+ # @return [void] This method outputs the formatted tag distribution to the console.
80
+ def format_tag_distribution(duration_by_tag)
81
+ block = '▅'
82
+ total = duration_by_tag.values.sum
83
+ return unless total.positive?
84
+
85
+ factor = duration_by_tag.size < 3 ? 2 : 1
86
+ sorted_duration_by_tag = duration_by_tag.sort_by { |_, duration| -duration }
87
+
88
+ sorted_duration_by_tag.each do |tag, duration|
89
+ value = (duration.to_f / total * 100).round(2)
90
+ puts "#{tag.rjust(8)}: #{value.to_s.rjust(7)}% \u001b[38;5;#{rand(256)}m#{block * (value / factor).to_i}\u001b[0m"
91
+ end
92
+ end
93
+
94
+ # Prints the entire time block chart.
95
+ #
96
+ # This method orchestrates the printing of the entire time block chart by calling
97
+ # the `print_header` and `print_blocks` methods. It also prints the separator line
98
+ # between the header and the blocks, and adds a double newline at the end for
99
+ # separation.
100
+ #
101
+ # @param time_block [Hash] A hash where the keys are formatted hour strings
102
+ # (e.g., "00", "01") and the values are the corresponding
103
+ # values to determine the block character.
104
+ # @example
105
+ # time_block = { "00" => 100, "01" => 200, ..., "23" => 300 }
106
+ # print_time_block_chart(time_block)
107
+ # # Output:
108
+ # # ⏳ ↦ ┏ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
109
+ # # ┗ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
110
+ # #
111
+ # # (followed by two newlines)
112
+ #
113
+ def print_time_block_chart(time_block)
114
+ print_header
115
+ print ' ┗ '
116
+ print_blocks(time_block)
117
+ end
118
+
119
+ # Prints the header of the time block chart.
120
+ #
121
+ # This method outputs the header line of the chart, which includes the hours
122
+ # from 00 to 23, formatted and aligned for readability.
123
+ #
124
+ # @example
125
+ # print_header
126
+ # # Output:
127
+ # # ⏳ ↦ ┏ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
128
+ #
129
+ def print_header
130
+ puts
131
+ print '⏳ ↦ ┏ '
132
+ (0..23).each { |hour| print format('%02d', hour).ljust(4) }
133
+ puts
134
+ end
135
+
136
+ # Prints the block characters for each hour in the time block chart.
137
+ #
138
+ # This method iterates over each hour from 0 to 23, retrieves the corresponding
139
+ # block character using the `get_block_char` method, and prints it aligned for
140
+ # readability. It also adds a double newline at the end for separation.
141
+ #
142
+ # @param time_block [Hash] A hash where the keys are formatted hour strings
143
+ # (e.g., "00", "01") and the values are the corresponding
144
+ # values to determine the block character.
145
+ # @example
146
+ # time_block = { "00" => 100, "01" => 200, ..., "23" => 300 }
147
+ # print_blocks(time_block)
148
+ # # Output:
149
+ # # ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █
150
+ # #
151
+ # # (followed by two newlines)
152
+ #
153
+ def print_blocks(time_block)
154
+ return unless time_block
155
+
156
+ (0..23).each do |hour|
157
+ block_char = get_block_char(time_block[format('%02d', hour)])
158
+ print (block_char * 2).ljust(4)
159
+ end
160
+ puts "\n\n"
161
+ end
162
+
163
+ # Determines the block character based on the value.
164
+ #
165
+ # @param value [Integer] The value to determine the block character for.
166
+ # @return [String] The block character corresponding to the value.
167
+ def get_block_char(value)
168
+ range_to_char = {
169
+ 0..120 => ' ',
170
+ 121..450 => '▁',
171
+ 451..900 => '▂',
172
+ 901..1350 => '▃',
173
+ 1351..1800 => '▄',
174
+ 1801..2250 => '▅',
175
+ 2251..2700 => '▆',
176
+ 2701..3150 => '▇',
177
+ 3151..3600 => '█'
178
+ }
179
+
180
+ range_to_char.find { |range, _| range.include?(value) }&.last || ' '
181
+ end
67
182
  end
68
183
  end
@@ -168,5 +168,71 @@ module Timet
168
168
  def self.current_timestamp
169
169
  Time.now.utc.to_i
170
170
  end
171
+
172
+ # Counts the number of seconds for each hour block between the given start and end times.
173
+ #
174
+ # This method calculates the number of seconds each event spans within each hour block
175
+ # and aggregates the results in a hash where the keys are the hour blocks (in 'HH' format)
176
+ # and the values are the total number of seconds for each hour block.
177
+ #
178
+ # @param start_time [Integer] The start time in seconds since the Unix epoch.
179
+ # @param end_time [Integer] The end time in seconds since the Unix epoch. If not provided,
180
+ # the current time will be used.
181
+ # @return [Hash] A hash where the keys are the hour blocks (in 'HH' format) and the values
182
+ # are the total number of seconds for each hour block.
183
+ # @example
184
+ # start_time = 1728577349 # 8:30 AM
185
+ # end_time = 1728579200 # 11:20 AM
186
+ # result = count_seconds_per_hour_block(start_time, end_time)
187
+ # # Output: {"08"=>1800, "09"=>1800, "10"=>3600, "11"=>1200}
188
+ #
189
+ def self.count_seconds_per_hour_block(start_time, end_time)
190
+ hour_blocks = Hash.new(0)
191
+
192
+ current_time = Time.at(start_time)
193
+ end_time = Time.at(end_time || current_timestamp)
194
+
195
+ while current_time < end_time
196
+ current_hour = current_time.hour
197
+ next_hour_boundary = Time.new(current_time.year, current_time.month, current_time.day, current_hour + 1)
198
+
199
+ block_end_time = [next_hour_boundary, end_time].min
200
+ seconds_in_block = (block_end_time - current_time).to_i
201
+
202
+ hour_block = current_time.strftime('%H')
203
+ hour_blocks[hour_block] += seconds_in_block
204
+
205
+ current_time = block_end_time
206
+ end
207
+
208
+ hour_blocks
209
+ end
210
+
211
+ # Aggregates the values of the same keys from an array of hashes.
212
+ #
213
+ # This method takes an array of hashes, reverses it, and then aggregates the values
214
+ # for the same keys into a single hash. If a key appears in multiple hashes, its
215
+ # values are summed.
216
+ #
217
+ # @param time_block [Array<Hash>] An array of hashes where each hash contains key-value pairs.
218
+ # @return [Hash] A hash where the keys are the aggregated keys from the input hashes
219
+ # and the values are the summed values for each key.
220
+ # @example
221
+ # time_block = [
222
+ # {"01": 10},
223
+ # {"01": 30},
224
+ # {"02": 50}
225
+ # ]
226
+ # result = aggregate_hash_values(time_block)
227
+ # # Output: {"01"=>40, "02"=>50}
228
+ #
229
+ def self.aggregate_hash_values(time_block)
230
+ time_block.reverse.each_with_object({}) do |hash, acc|
231
+ hash.each do |key, value|
232
+ acc[key] ||= 0
233
+ acc[key] += value
234
+ end
235
+ end
236
+ end
171
237
  end
172
238
  end
@@ -51,12 +51,23 @@ module Timet
51
51
  return puts 'No tracked time found for the specified filter.' if items.empty?
52
52
 
53
53
  format_table_header
54
+ duration_by_tag = Hash.new(0)
55
+ time_block = []
54
56
  items.each_with_index do |item, idx|
55
57
  date = TimeHelper.extract_date(items, idx)
56
58
  display_time_entry(item, date)
59
+ time_block << TimeHelper.count_seconds_per_hour_block(item[1], item[2])
60
+ duration_by_tag[item[3]] += TimeHelper.calculate_duration(item[1], item[2])
57
61
  end
58
62
  puts format_table_separator
59
63
  total
64
+
65
+ if Time.now.to_i - items.map { |x| x[1] }.min < 86_400
66
+ time_block_reverse = TimeHelper.aggregate_hash_values(time_block)
67
+ print_time_block_chart(time_block_reverse)
68
+ end
69
+
70
+ format_tag_distribution(duration_by_tag)
60
71
  end
61
72
 
62
73
  # Displays a single row of the report.
data/lib/timet/version.rb CHANGED
@@ -6,6 +6,6 @@ module Timet
6
6
  # @return [String] The version number in the format 'major.minor.patch'.
7
7
  #
8
8
  # @example Get the version of the Timet application
9
- # Timet::VERSION # => '1.0.0'
10
- VERSION = '1.0.0'
9
+ # Timet::VERSION # => '1.2.0'
10
+ VERSION = '1.2.0'
11
11
  end
data/timet.webp ADDED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timet
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Vielma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-07 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -63,6 +63,7 @@ email:
63
63
  - frankvielma@gmail.com
64
64
  executables:
65
65
  - timet
66
+ - tt
66
67
  extensions: []
67
68
  extra_rdoc_files: []
68
69
  files:
@@ -75,6 +76,7 @@ files:
75
76
  - README.md
76
77
  - Rakefile
77
78
  - bin/timet
79
+ - bin/tt
78
80
  - btc.png
79
81
  - lib/timet.rb
80
82
  - lib/timet/application.rb
@@ -87,6 +89,7 @@ files:
87
89
  - lib/timet/validation_edit_helper.rb
88
90
  - lib/timet/version.rb
89
91
  - sig/timet.rbs
92
+ - timet.webp
90
93
  homepage: https://frankvielma.github.io/posts/timet-a-powerful-command-line-tool-for-tracking-your-time/
91
94
  licenses:
92
95
  - MIT