tasku 0.2.2 → 0.3.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 +4 -4
- data/exe/tasku +5 -1
- data/lib/tasku/cli.rb +96 -7
- data/lib/tasku/config.rb +52 -0
- data/lib/tasku/database.rb +5 -0
- data/lib/tasku/output/terminal.rb +70 -10
- data/lib/tasku/project.rb +13 -0
- data/lib/tasku/version.rb +1 -1
- data/lib/tasku.rb +7 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7cbf11fec02c7ece6e6d6599f6a7b6e1dc837cae43f9683a197e63c3ca37cc58
|
|
4
|
+
data.tar.gz: 98582ff1f4d450d8d5d669e245aca8fc9ae7704fddbe0c661852b6f2a82020b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caecee96bc181eb9ca32c6fa67534ca82950cfba81c78c26d6e318f461bdc925d73446d94e88fbe91ebf15d69c3c82e0f281ab81770b4307d217e54781008e6d
|
|
7
|
+
data.tar.gz: 13e9c104b1c50404af47972a9012e731d0aec773ebdb776e6122eecf466f30e7c7f7642889df6b41685eeeb04f3d0ed2876fa574bfed92a2d1e7f859df3e7562
|
data/exe/tasku
CHANGED
data/lib/tasku/cli.rb
CHANGED
|
@@ -13,6 +13,40 @@ module Tasku
|
|
|
13
13
|
|
|
14
14
|
TAGLINE = "\u30BF\u30B9\u30AF\u30EA\u30B9\u30C8 \u2014 terminal task manager"
|
|
15
15
|
|
|
16
|
+
class ConfigApp < Thor
|
|
17
|
+
desc "list", "Show all preferences and their current values"
|
|
18
|
+
def list
|
|
19
|
+
pastel = Pastel.new
|
|
20
|
+
puts ""
|
|
21
|
+
Tasku::Config::VALID_KEYS.each do |key, meta|
|
|
22
|
+
current = Tasku::Config.get(key)
|
|
23
|
+
options_str = meta[:values].map { |v| v == current ? pastel.bold(pastel.green(v)) : pastel.dim(v) }.join(", ")
|
|
24
|
+
puts " #{pastel.bold(key.ljust(20))} #{options_str} #{pastel.dim("— #{meta[:description]}")}"
|
|
25
|
+
end
|
|
26
|
+
puts ""
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "set KEY VALUE", "Set a preference value"
|
|
30
|
+
def set(key, value)
|
|
31
|
+
pastel = Pastel.new
|
|
32
|
+
meta = Tasku::Config::VALID_KEYS[key]
|
|
33
|
+
abort pastel.red("Unknown preference '#{key}'. Run `tasku config list` to see available keys.") unless meta
|
|
34
|
+
unless meta[:values].include?(value)
|
|
35
|
+
abort pastel.red("Invalid value '#{value}' for '#{key}'. Valid: #{meta[:values].join(', ')}")
|
|
36
|
+
end
|
|
37
|
+
Tasku::Config.set(key, value)
|
|
38
|
+
puts pastel.green(" ✓ #{key} set to '#{value}'.")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "get KEY", "Get the current value of a preference"
|
|
42
|
+
def get(key)
|
|
43
|
+
pastel = Pastel.new
|
|
44
|
+
meta = Tasku::Config::VALID_KEYS[key]
|
|
45
|
+
abort pastel.red("Unknown preference '#{key}'. Run `tasku config list` to see available keys.") unless meta
|
|
46
|
+
puts " #{key}: #{pastel.bold(Tasku::Config.get(key))}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
16
50
|
class App < Thor
|
|
17
51
|
def self.start(args = ARGV, **opts)
|
|
18
52
|
sql_index = args.index("--sql")
|
|
@@ -33,6 +67,19 @@ module Tasku
|
|
|
33
67
|
|
|
34
68
|
class_option :db, type: :string, desc: "Path to SQLite database (default: ~/.tasku/tasks.db)", hide: true
|
|
35
69
|
|
|
70
|
+
desc "config SUBCOMMAND", "Manage user preferences"
|
|
71
|
+
subcommand "config", ConfigApp
|
|
72
|
+
|
|
73
|
+
def help(*args)
|
|
74
|
+
if args.empty?
|
|
75
|
+
puts ""
|
|
76
|
+
puts pastel.bold(LOGO)
|
|
77
|
+
puts " #{pastel.bold(TAGLINE)}"
|
|
78
|
+
puts ""
|
|
79
|
+
end
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
|
|
36
83
|
desc "sql QUERY", "Run a raw SQL query against the database"
|
|
37
84
|
def sql(query)
|
|
38
85
|
if query.strip.upcase.match?(/\A(SELECT|PRAGMA|EXPLAIN)/)
|
|
@@ -157,13 +204,14 @@ module Tasku
|
|
|
157
204
|
dataset = dataset.order(order)
|
|
158
205
|
|
|
159
206
|
tasks = dataset.all
|
|
160
|
-
|
|
207
|
+
bar = { "bar_project" => Config.get("bar_project"), "bar_priority" => Config.get("bar_priority"), "bar_status" => Config.get("bar_status") }
|
|
208
|
+
terminal.render_list(tasks, colour_map: Project.colour_map, spacing: Config.get("list_spacing"), bar: bar)
|
|
161
209
|
end
|
|
162
210
|
|
|
163
211
|
desc "show ID", "Show task details"
|
|
164
212
|
def show(id)
|
|
165
213
|
task = find_task(id)
|
|
166
|
-
terminal.render_show(task)
|
|
214
|
+
terminal.render_show(task, colour_map: Project.colour_map)
|
|
167
215
|
end
|
|
168
216
|
|
|
169
217
|
desc "edit ID", "Edit a task"
|
|
@@ -241,25 +289,48 @@ module Tasku
|
|
|
241
289
|
desc "stats", "Show task statistics"
|
|
242
290
|
def stats
|
|
243
291
|
tasks = Task.dataset.all
|
|
244
|
-
terminal.render_stats(tasks) if tasks
|
|
292
|
+
terminal.render_stats(tasks, colour_map: Project.colour_map) if tasks
|
|
245
293
|
end
|
|
246
294
|
|
|
247
295
|
desc "projects", "List all projects"
|
|
248
296
|
def projects
|
|
249
|
-
|
|
250
|
-
if
|
|
297
|
+
project_names = Task.dataset.select(:project).where(Sequel.~(project: nil)).distinct.order(:project).map(:project)
|
|
298
|
+
if project_names.empty?
|
|
251
299
|
puts pastel.yellow(" No projects found.")
|
|
252
300
|
return
|
|
253
301
|
end
|
|
254
302
|
|
|
303
|
+
colour_map = Project.colour_map
|
|
255
304
|
puts ""
|
|
256
|
-
|
|
305
|
+
project_names.each do |p|
|
|
257
306
|
count = Task.where(project: p).count
|
|
258
|
-
|
|
307
|
+
colour = colour_map[p]
|
|
308
|
+
swatch = colour ? "#{hex_colour_str("█", colour)} " : " "
|
|
309
|
+
name_str = colour ? hex_bold_str(p, colour) : pastel.bold(p)
|
|
310
|
+
puts " #{swatch}#{name_str} #{pastel.dim("(#{count} task(s))")}"
|
|
259
311
|
end
|
|
260
312
|
puts ""
|
|
261
313
|
end
|
|
262
314
|
|
|
315
|
+
desc "colour PROJECT HEX", "Set a project's display colour (e.g. #FF5733). Omit HEX to clear."
|
|
316
|
+
def colour(project, hex = nil)
|
|
317
|
+
if hex.nil?
|
|
318
|
+
proj = Project[project]
|
|
319
|
+
if proj
|
|
320
|
+
proj.update(colour: nil)
|
|
321
|
+
puts pastel.green(" Colour cleared for project '#{project}'.")
|
|
322
|
+
else
|
|
323
|
+
puts pastel.yellow(" No colour set for project '#{project}'.")
|
|
324
|
+
end
|
|
325
|
+
return
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
validated = validate_hex!(hex)
|
|
329
|
+
Project.find_or_create(name: project).update(colour: validated)
|
|
330
|
+
swatch = hex_colour_str("█", validated)
|
|
331
|
+
puts " #{swatch} Colour #{pastel.bold(validated)} set for project '#{pastel.bold(project)}'."
|
|
332
|
+
end
|
|
333
|
+
|
|
263
334
|
desc "categories", "List all categories"
|
|
264
335
|
def categories
|
|
265
336
|
categories = Task.dataset.select(:category).where(Sequel.~(category: nil)).distinct.order(:category).map(:category)
|
|
@@ -428,6 +499,24 @@ module Tasku
|
|
|
428
499
|
"archived" => :dim
|
|
429
500
|
}.freeze
|
|
430
501
|
|
|
502
|
+
def validate_hex!(hex)
|
|
503
|
+
hex = hex.start_with?("#") ? hex : "##{hex}"
|
|
504
|
+
unless hex.match?(/\A#[0-9a-fA-F]{6}\z/)
|
|
505
|
+
abort pastel.red("Invalid hex colour '#{hex}'. Use format #RRGGBB.")
|
|
506
|
+
end
|
|
507
|
+
hex
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def hex_colour_str(text, hex)
|
|
511
|
+
r, g, b = hex.delete("#").scan(/../).map { |c| c.to_i(16) }
|
|
512
|
+
"\e[38;2;#{r};#{g};#{b}m#{text}\e[0m"
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def hex_bold_str(text, hex)
|
|
516
|
+
r, g, b = hex.delete("#").scan(/../).map { |c| c.to_i(16) }
|
|
517
|
+
"\e[1;38;2;#{r};#{g};#{b}m#{text}\e[0m"
|
|
518
|
+
end
|
|
519
|
+
|
|
431
520
|
def colour_cell(col, padded_val, row)
|
|
432
521
|
case col.to_s
|
|
433
522
|
when "priority"
|
data/lib/tasku/config.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Tasku
|
|
7
|
+
class Config
|
|
8
|
+
CONFIG_PATH = File.join(Dir.home, ".tasku", "config.json")
|
|
9
|
+
|
|
10
|
+
VALID_KEYS = {
|
|
11
|
+
"list_spacing" => {
|
|
12
|
+
values: %w[compact spacious],
|
|
13
|
+
default: "compact",
|
|
14
|
+
description: "Row spacing in task list"
|
|
15
|
+
},
|
|
16
|
+
"bar_project" => {
|
|
17
|
+
values: %w[on off],
|
|
18
|
+
default: "on",
|
|
19
|
+
description: "Show project colour segment in row bar"
|
|
20
|
+
},
|
|
21
|
+
"bar_priority" => {
|
|
22
|
+
values: %w[on off],
|
|
23
|
+
default: "on",
|
|
24
|
+
description: "Show priority colour segment in row bar"
|
|
25
|
+
},
|
|
26
|
+
"bar_status" => {
|
|
27
|
+
values: %w[on off],
|
|
28
|
+
default: "on",
|
|
29
|
+
description: "Show status colour segment in row bar"
|
|
30
|
+
}
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
def self.get(key)
|
|
34
|
+
all[key] || VALID_KEYS.dig(key, :default)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.set(key, value)
|
|
38
|
+
current = all
|
|
39
|
+
current[key] = value
|
|
40
|
+
FileUtils.mkdir_p(File.dirname(CONFIG_PATH))
|
|
41
|
+
File.write(CONFIG_PATH, JSON.pretty_generate(current))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.all
|
|
45
|
+
return {} unless File.exist?(CONFIG_PATH)
|
|
46
|
+
|
|
47
|
+
JSON.parse(File.read(CONFIG_PATH))
|
|
48
|
+
rescue JSON::ParserError
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/tasku/database.rb
CHANGED
|
@@ -42,6 +42,11 @@ module Tasku
|
|
|
42
42
|
if db.table_exists?(:tasks) && !db.schema(:tasks).map(&:first).include?(:code)
|
|
43
43
|
db.alter_table(:tasks) { add_column :code, String }
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
db.create_table? :projects do
|
|
47
|
+
String :name, primary_key: true
|
|
48
|
+
String :colour
|
|
49
|
+
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
47
52
|
end
|
|
@@ -5,6 +5,23 @@ require "pastel"
|
|
|
5
5
|
module Tasku
|
|
6
6
|
module Output
|
|
7
7
|
class Terminal
|
|
8
|
+
PRIORITY_HEX = {
|
|
9
|
+
"none" => "#6B7280",
|
|
10
|
+
"low" => "#06B6D4",
|
|
11
|
+
"medium" => "#EAB308",
|
|
12
|
+
"high" => "#EF4444",
|
|
13
|
+
"urgent" => "#E879F9"
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
STATUS_HEX = {
|
|
17
|
+
"backlog" => "#6B7280",
|
|
18
|
+
"todo" => "#3B82F6",
|
|
19
|
+
"in_progress" => "#EAB308",
|
|
20
|
+
"done" => "#22C55E",
|
|
21
|
+
"cancelled" => "#EF4444",
|
|
22
|
+
"archived" => "#6B7280"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
8
25
|
PRIORITY_STYLES = {
|
|
9
26
|
"none" => { color: :dim, symbol: " " },
|
|
10
27
|
"low" => { color: :cyan, symbol: "↓" },
|
|
@@ -29,13 +46,13 @@ module Tasku
|
|
|
29
46
|
@term_width = [terminal_width, 80].min
|
|
30
47
|
end
|
|
31
48
|
|
|
32
|
-
def render_list(tasks)
|
|
49
|
+
def render_list(tasks, colour_map: {}, spacing: "compact", bar: {})
|
|
33
50
|
if tasks.empty?
|
|
34
51
|
puts @pastel.yellow(" No tasks found.")
|
|
35
52
|
return
|
|
36
53
|
end
|
|
37
54
|
|
|
38
|
-
rows = tasks.map { |t| build_columns(t) }
|
|
55
|
+
rows = tasks.map { |t| build_columns(t, colour_map, bar) }
|
|
39
56
|
col_widths = compute_widths(rows)
|
|
40
57
|
total = col_widths.sum + (COL_SEP.length * (col_widths.length - 1))
|
|
41
58
|
|
|
@@ -44,12 +61,13 @@ module Tasku
|
|
|
44
61
|
rows.each do |cols|
|
|
45
62
|
line = cols.each_with_index.map { |c, i| c.to_s.ljust(col_widths[i]) }.join(COL_SEP)
|
|
46
63
|
puts " #{line}"
|
|
64
|
+
puts "" if spacing == "spacious"
|
|
47
65
|
end
|
|
48
66
|
puts @pastel.dim(" #{"─" * total}")
|
|
49
67
|
puts @pastel.dim(" #{tasks.length} task(s) found")
|
|
50
68
|
end
|
|
51
69
|
|
|
52
|
-
def render_show(task)
|
|
70
|
+
def render_show(task, colour_map: {})
|
|
53
71
|
puts ""
|
|
54
72
|
puts @pastel.bold(" Task ##{task.id}")
|
|
55
73
|
puts @pastel.dim(" #{"─" * 60}")
|
|
@@ -57,7 +75,7 @@ module Tasku
|
|
|
57
75
|
fields = [
|
|
58
76
|
["Name", @pastel.bold(task.name)],
|
|
59
77
|
["Description", task.description ? @pastel.dim(task.description) : @pastel.dim("—")],
|
|
60
|
-
["Project", task.project
|
|
78
|
+
["Project", project_str(task.project, colour_map)],
|
|
61
79
|
["Category", task.category || @pastel.dim("—")],
|
|
62
80
|
["Priority", priority_tag(task.priority)],
|
|
63
81
|
["Status", status_tag(task.status)],
|
|
@@ -90,7 +108,7 @@ module Tasku
|
|
|
90
108
|
puts @pastel.red(" ✗ Task ##{task.id} deleted") + " — #{@pastel.bold(task.name)}"
|
|
91
109
|
end
|
|
92
110
|
|
|
93
|
-
def render_stats(tasks)
|
|
111
|
+
def render_stats(tasks, colour_map: {})
|
|
94
112
|
puts ""
|
|
95
113
|
puts @pastel.bold(" Task Statistics")
|
|
96
114
|
puts @pastel.dim(" #{"─" * 60}")
|
|
@@ -98,12 +116,13 @@ module Tasku
|
|
|
98
116
|
total = tasks.length
|
|
99
117
|
by_status = tasks.group_by(&:status).transform_values(&:length)
|
|
100
118
|
by_priority = tasks.group_by(&:priority).transform_values(&:length)
|
|
119
|
+
by_project = tasks.group_by(&:project).transform_values(&:length)
|
|
101
120
|
overdue = tasks.count(&:overdue?)
|
|
102
|
-
|
|
121
|
+
project_count = tasks.map(&:project).compact.uniq.length
|
|
103
122
|
|
|
104
123
|
puts " #{@pastel.dim("Total tasks:".ljust(20))} #{total}"
|
|
105
124
|
puts " #{@pastel.dim("Overdue:".ljust(20))} #{overdue.positive? ? @pastel.red(overdue.to_s) : @pastel.green("0")}"
|
|
106
|
-
puts " #{@pastel.dim("Projects:".ljust(20))} #{
|
|
125
|
+
puts " #{@pastel.dim("Projects:".ljust(20))} #{project_count}"
|
|
107
126
|
puts ""
|
|
108
127
|
|
|
109
128
|
puts " #{@pastel.dim("By Status:")}"
|
|
@@ -122,16 +141,42 @@ module Tasku
|
|
|
122
141
|
|
|
123
142
|
puts " #{priority_tag(p)} #{@pastel.send(PRIORITY_STYLES.dig(p, :color) || :dim, count.to_s.rjust(3))}"
|
|
124
143
|
end
|
|
144
|
+
|
|
145
|
+
puts ""
|
|
146
|
+
puts " #{@pastel.dim("By Project:")}"
|
|
147
|
+
by_project.sort_by { |_, count| -count }.each do |name, count|
|
|
148
|
+
label = name || @pastel.dim("(none)")
|
|
149
|
+
coloured_name = name && colour_map[name] ? hex_colour_str(name, colour_map[name]) : label
|
|
150
|
+
padding = " " * [20 - strip_ansi(coloured_name).length, 0].max
|
|
151
|
+
puts " #{coloured_name}#{padding} #{@pastel.dim(count.to_s.rjust(3))}"
|
|
152
|
+
end
|
|
125
153
|
puts ""
|
|
126
154
|
end
|
|
127
155
|
|
|
128
156
|
private
|
|
129
157
|
|
|
130
|
-
def build_columns(task)
|
|
158
|
+
def build_columns(task, colour_map = {}, bar = {})
|
|
131
159
|
id_val = task.code && !task.code.empty? ? "#{task.code}-#{task.id}" : task.id.to_s
|
|
132
|
-
|
|
160
|
+
|
|
161
|
+
segments = []
|
|
162
|
+
if bar["bar_project"] != "off"
|
|
163
|
+
proj_colour = task.project && colour_map[task.project]
|
|
164
|
+
segments << (proj_colour ? hex_colour_str("█", proj_colour) : @pastel.dim("█"))
|
|
165
|
+
end
|
|
166
|
+
if bar["bar_priority"] != "off"
|
|
167
|
+
segments << hex_colour_str("█", PRIORITY_HEX[task.priority] || PRIORITY_HEX["none"])
|
|
168
|
+
end
|
|
169
|
+
if bar["bar_status"] != "off"
|
|
170
|
+
segments << hex_colour_str("█", STATUS_HEX[task.status] || STATUS_HEX["backlog"])
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
id_str = if segments.any?
|
|
174
|
+
"#{segments.join} #{@pastel.dim(id_val)}"
|
|
175
|
+
else
|
|
176
|
+
@pastel.dim(id_val)
|
|
177
|
+
end
|
|
133
178
|
name_str = @pastel.bold(task.name)
|
|
134
|
-
proj_str = task.project
|
|
179
|
+
proj_str = project_str(task.project, colour_map)
|
|
135
180
|
prio_str = priority_tag(task.priority)
|
|
136
181
|
stat_str = status_tag(task.status)
|
|
137
182
|
due_str = due_cell(task)
|
|
@@ -181,6 +226,21 @@ module Tasku
|
|
|
181
226
|
@pastel.send(style[:color], "#{style[:symbol]} #{label}")
|
|
182
227
|
end
|
|
183
228
|
|
|
229
|
+
def hex_colour_str(text, hex)
|
|
230
|
+
r, g, b = hex.delete("#").scan(/../).map { |c| c.to_i(16) }
|
|
231
|
+
"\e[38;2;#{r};#{g};#{b}m#{text}\e[0m"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def project_str(project, colour_map)
|
|
235
|
+
return @pastel.dim("—") unless project
|
|
236
|
+
|
|
237
|
+
colour = colour_map[project]
|
|
238
|
+
return project unless colour
|
|
239
|
+
|
|
240
|
+
r, g, b = colour.delete("#").scan(/../).map { |c| c.to_i(16) }
|
|
241
|
+
"\e[38;2;#{r};#{g};#{b}m#{project}\e[0m"
|
|
242
|
+
end
|
|
243
|
+
|
|
184
244
|
def due_cell(task)
|
|
185
245
|
return @pastel.dim("—") unless task.due_day
|
|
186
246
|
|
data/lib/tasku/version.rb
CHANGED
data/lib/tasku.rb
CHANGED
|
@@ -8,6 +8,12 @@ require_relative "tasku/version"
|
|
|
8
8
|
require_relative "tasku/database"
|
|
9
9
|
Tasku::Database.connect
|
|
10
10
|
require_relative "tasku/task"
|
|
11
|
+
require_relative "tasku/project"
|
|
12
|
+
require_relative "tasku/config"
|
|
11
13
|
require_relative "tasku/output/terminal"
|
|
12
14
|
require_relative "tasku/cli"
|
|
13
|
-
|
|
15
|
+
begin
|
|
16
|
+
require_relative "tasku/tui/app"
|
|
17
|
+
rescue LoadError
|
|
18
|
+
# TUI not available in this build
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tasku
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom Dringer
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-07-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: thor
|
|
@@ -89,8 +89,10 @@ files:
|
|
|
89
89
|
- exe/tasku
|
|
90
90
|
- lib/tasku.rb
|
|
91
91
|
- lib/tasku/cli.rb
|
|
92
|
+
- lib/tasku/config.rb
|
|
92
93
|
- lib/tasku/database.rb
|
|
93
94
|
- lib/tasku/output/terminal.rb
|
|
95
|
+
- lib/tasku/project.rb
|
|
94
96
|
- lib/tasku/task.rb
|
|
95
97
|
- lib/tasku/version.rb
|
|
96
98
|
homepage: https://github.com/tomdringer/tasku
|