letsdo 0.1.0 → 0.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 +4 -4
- data/README.md +4 -4
- data/bin/letsdo +20 -12
- data/lib/letsdo/agent.rb +6 -4
- data/lib/letsdo/agent_loop/tasks.rb +62 -0
- data/lib/letsdo/agent_loop.rb +41 -126
- data/lib/letsdo/backlog_tasks.rb +10 -11
- data/lib/letsdo/capture.rb +89 -0
- data/lib/letsdo/cli/init.rb +33 -0
- data/lib/letsdo/cli/launch.rb +82 -0
- data/lib/letsdo/cli.rb +64 -124
- data/lib/letsdo/control.rb +39 -0
- data/lib/letsdo/default_prompt.rb +69 -0
- data/lib/letsdo/errors.rb +2 -12
- data/lib/letsdo/loop.rb +31 -15
- data/lib/letsdo/output_streamer/arg_summary.rb +81 -0
- data/lib/letsdo/output_streamer.rb +13 -210
- data/lib/letsdo/pi_runner/events.rb +75 -0
- data/lib/letsdo/pi_runner/process.rb +68 -0
- data/lib/letsdo/pi_runner.rb +62 -221
- data/lib/letsdo/prompt_store.rb +47 -8
- data/lib/letsdo/tui/input.rb +4 -4
- data/lib/letsdo/tui/log_buffer.rb +3 -3
- data/lib/letsdo/tui/metrics.rb +1 -1
- data/lib/letsdo/tui/renderer.rb +103 -80
- data/lib/letsdo/tui/session/keys.rb +121 -0
- data/lib/letsdo/tui/session/view.rb +89 -0
- data/lib/letsdo/tui/session.rb +40 -183
- data/lib/letsdo/tui/terminal.rb +7 -4
- data/lib/letsdo/tui.rb +6 -6
- data/lib/letsdo/version.rb +2 -2
- data/lib/letsdo.rb +19 -13
- metadata +31 -1
data/lib/letsdo/tui/session.rb
CHANGED
|
@@ -1,90 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'session/keys'
|
|
4
|
+
require_relative 'session/view'
|
|
5
|
+
|
|
3
6
|
module Letsdo
|
|
4
7
|
module Tui
|
|
5
|
-
# The TUI controller:
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Threading model (the orchestrator loop stays on the calling thread,
|
|
9
|
-
# exactly as in plain mode — Letsdo::PiRunner's signal design keeps
|
|
10
|
-
# working unchanged):
|
|
11
|
-
# main thread — the injected block (Letsdo::AgentLoop#run with its
|
|
12
|
-
# own signal traps): provider queries, agent runs,
|
|
13
|
-
# streamer writes into the log, metrics events;
|
|
14
|
-
# input thread — the SOLE repainter: polls keys (tty-reader),
|
|
15
|
-
# drains the log snapshot, tracks the metrics
|
|
16
|
-
# snapshot and repaints on keys, data, 1s timer
|
|
17
|
-
# ticks and SIGWINCH. Never touches the loop.
|
|
18
|
-
#
|
|
19
|
-
# Interactive actions (footer lists them):
|
|
20
|
-
# ↑ / ↓ / PgUp / PgDn / Home / End — scroll; the view auto-follows
|
|
21
|
-
# the newest line while at the bottom (tail -f semantics);
|
|
22
|
-
# p — pause/resume: display freeze (PAUSED in the header, the log
|
|
23
|
-
# keeps buffering, the view stays); keys still repaint;
|
|
24
|
-
# r — immediate backlog re-query for the 'left' metric;
|
|
25
|
-
# q / Ctrl-C — quit: exactly the signal-stop unwinding — Letsdo::Stopped
|
|
26
|
-
# raised into the main thread terminates the pi child (PiRunner
|
|
27
|
-
# rescue), stops the loop (AgentLoop rescue) and restores the
|
|
28
|
-
# terminal from the ensure block; exit code 0.
|
|
29
|
-
#
|
|
30
|
-
# Signals: SIGWINCH sets a flag → the input thread re-queries the size
|
|
31
|
-
# and repaints (no corruption). SIGINT/SIGTERM keep working through the
|
|
32
|
-
# loop's own traps (main thread).
|
|
8
|
+
# The TUI controller: terminal lifecycle, background input thread, and
|
|
9
|
+
# the repaint loop around an orchestrator run.
|
|
33
10
|
class Session
|
|
34
|
-
|
|
11
|
+
include SessionKeys
|
|
12
|
+
include SessionView
|
|
13
|
+
|
|
35
14
|
REPAINT_INTERVAL = 1.0
|
|
36
|
-
# Poll sleep when the display is frozen or nothing happened (avoids a
|
|
37
|
-
# busy loop while still repainting at the timer interval).
|
|
38
15
|
IDLE_SLEEP = 0.01
|
|
39
|
-
# How long teardown waits for the input thread to finish its poll.
|
|
40
16
|
INPUT_JOIN_TIMEOUT = 2.0
|
|
41
17
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# @param input [Input] the key reader
|
|
48
|
-
# @param refresh [Proc, nil] callable → open task count (Integer) or
|
|
49
|
-
# nil (backlog unreadable); 'r' uses it for an immediate
|
|
50
|
-
# re-query
|
|
51
|
-
# @param wait_seconds [Numeric] retry interval for the waiting state
|
|
52
|
-
# @param clock [Proc] monotonic clock for tick timing (injectable)
|
|
53
|
-
def initialize(name:, handle:, log:, metrics:, terminal:, input:,
|
|
54
|
-
refresh: nil, wait_seconds: 10.0, clock: nil)
|
|
55
|
-
@name = name
|
|
56
|
-
@handle = handle
|
|
57
|
-
@log = log
|
|
58
|
-
@metrics = metrics
|
|
59
|
-
@terminal = terminal
|
|
60
|
-
@input = input
|
|
61
|
-
@refresh = refresh
|
|
62
|
-
@wait_seconds = wait_seconds
|
|
63
|
-
@clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
64
|
-
@offset = 0
|
|
65
|
-
@follow = true
|
|
66
|
-
@paused = false
|
|
67
|
-
@stop = false
|
|
68
|
-
@winch = false
|
|
69
|
-
@body_height = 1
|
|
70
|
-
@seen_version = 0
|
|
71
|
-
@input_thread = nil
|
|
18
|
+
def initialize(**opts)
|
|
19
|
+
assign_identity(opts)
|
|
20
|
+
assign_io(opts)
|
|
21
|
+
assign_controls(opts)
|
|
22
|
+
reset_view_state
|
|
72
23
|
end
|
|
73
24
|
|
|
74
|
-
# Runs the TUI around the given orchestrator work.
|
|
75
|
-
#
|
|
76
|
-
# The block runs on the calling thread. On quit (q/Ctrl-C) or a stop
|
|
77
|
-
# signal Letsdo::Stopped unwinds it; the terminal is restored from
|
|
78
|
-
# the ensure block on every exit path.
|
|
79
|
-
#
|
|
80
|
-
# @yield the orchestrator run (e.g. Letsdo::AgentLoop#run)
|
|
81
|
-
# @return [Integer] the block's result, or 0 when stopped
|
|
82
25
|
def run(&work)
|
|
83
26
|
install_winch_handler
|
|
84
27
|
@terminal.enter
|
|
85
28
|
start_input_thread
|
|
86
|
-
|
|
87
|
-
result
|
|
29
|
+
work.call
|
|
88
30
|
rescue Letsdo::Stopped
|
|
89
31
|
0
|
|
90
32
|
ensure
|
|
@@ -95,122 +37,37 @@ module Letsdo
|
|
|
95
37
|
|
|
96
38
|
private
|
|
97
39
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
loop do
|
|
104
|
-
break if @stop
|
|
105
|
-
|
|
106
|
-
key = @input.next_key
|
|
107
|
-
handled = key ? handle_key(key) : false
|
|
108
|
-
winch = take_winch
|
|
109
|
-
handled = true if winch
|
|
110
|
-
now = @clock.call
|
|
111
|
-
tick = now - @last_repaint >= REPAINT_INTERVAL
|
|
112
|
-
new_data = @log.version != @seen_version
|
|
113
|
-
if handled || (!@paused && (tick || new_data))
|
|
114
|
-
repaint
|
|
115
|
-
@last_repaint = @clock.call
|
|
116
|
-
else
|
|
117
|
-
sleep(IDLE_SLEEP)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
rescue StandardError
|
|
122
|
-
# The input thread must never die loudly into the middle of the
|
|
123
|
-
# screen, and a quiet exit still leaves the loop running (teardown
|
|
124
|
-
# joins it). Terminal state is restored by the ensure blocks.
|
|
125
|
-
nil
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def handle_key(key)
|
|
129
|
-
case key
|
|
130
|
-
when :up, :page_up
|
|
131
|
-
@offset -= key == :page_up ? page_step : 1
|
|
132
|
-
@follow = false
|
|
133
|
-
true
|
|
134
|
-
when :down, :page_down
|
|
135
|
-
@offset += key == :page_down ? page_step : 1
|
|
136
|
-
true # repaint decides re-sticking at the bottom
|
|
137
|
-
when :home
|
|
138
|
-
@offset = 0
|
|
139
|
-
@follow = false
|
|
140
|
-
true
|
|
141
|
-
when :end
|
|
142
|
-
@follow = true
|
|
143
|
-
true
|
|
144
|
-
when :p
|
|
145
|
-
@paused = !@paused
|
|
146
|
-
true
|
|
147
|
-
when :r
|
|
148
|
-
refresh
|
|
149
|
-
true
|
|
150
|
-
when :q, :ctrl_c
|
|
151
|
-
quit
|
|
152
|
-
false # leaving the session; no repaint needed
|
|
153
|
-
else
|
|
154
|
-
false
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Immediate backlog re-query: the provider is re-run on this thread
|
|
159
|
-
# (a subprocess call, thread-safe) and the count reaches the header
|
|
160
|
-
# snapshot.
|
|
161
|
-
def refresh
|
|
162
|
-
return unless @refresh
|
|
163
|
-
|
|
164
|
-
@metrics.provider_result(@refresh.call)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# Quit = the same unwinding as a stop signal: Letsdo::Stopped raised
|
|
168
|
-
# into the main thread (inside the orchestrator work) terminates the
|
|
169
|
-
# pi child and stops the loop; the ensure block restores the
|
|
170
|
-
# terminal. No-op after teardown already started.
|
|
171
|
-
def quit
|
|
172
|
-
return if @stop
|
|
173
|
-
|
|
174
|
-
Thread.main.raise(Letsdo::Stopped)
|
|
40
|
+
def assign_identity(opts)
|
|
41
|
+
@name = opts.fetch(:name)
|
|
42
|
+
@handle = opts.fetch(:handle)
|
|
43
|
+
@log = opts.fetch(:log)
|
|
44
|
+
@metrics = opts.fetch(:metrics)
|
|
175
45
|
end
|
|
176
46
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@
|
|
181
|
-
|
|
182
|
-
@
|
|
183
|
-
max_offset = Renderer.max_offset(lines, @body_height)
|
|
184
|
-
if @paused
|
|
185
|
-
@offset = [[@offset, max_offset].min, 0].max
|
|
186
|
-
elsif @follow
|
|
187
|
-
@offset = max_offset
|
|
188
|
-
else
|
|
189
|
-
@offset = [[@offset, max_offset].min, 0].max
|
|
190
|
-
@follow = true if @offset == max_offset
|
|
191
|
-
end
|
|
192
|
-
frame = Renderer.render(
|
|
193
|
-
metrics: @metrics.snapshot, lines: lines, width: width, height: height,
|
|
194
|
-
offset: @offset, follow: @follow, paused: @paused,
|
|
195
|
-
wait_seconds: @wait_seconds
|
|
196
|
-
)
|
|
197
|
-
@terminal.render(frame)
|
|
47
|
+
def assign_io(opts)
|
|
48
|
+
@terminal = opts.fetch(:terminal)
|
|
49
|
+
@input = opts.fetch(:input)
|
|
50
|
+
@refresh = opts[:refresh]
|
|
51
|
+
@wait_seconds = opts.fetch(:wait_seconds, 10.0)
|
|
52
|
+
@clock = opts[:clock] || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
198
53
|
end
|
|
199
54
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
@
|
|
55
|
+
def assign_controls(opts)
|
|
56
|
+
@pause_gate = opts[:pause_gate]
|
|
57
|
+
@runner = opts[:runner]
|
|
203
58
|
end
|
|
204
59
|
|
|
205
|
-
def
|
|
206
|
-
|
|
60
|
+
def reset_view_state
|
|
61
|
+
@offset = 0
|
|
62
|
+
@follow = true
|
|
63
|
+
@paused = false
|
|
64
|
+
@stop = false
|
|
207
65
|
@winch = false
|
|
208
|
-
|
|
66
|
+
@body_height = 1
|
|
67
|
+
@seen_version = 0
|
|
68
|
+
@input_thread = nil
|
|
209
69
|
end
|
|
210
70
|
|
|
211
|
-
# Raw keyboard mode for the whole input session (the tty-reader
|
|
212
|
-
# per-read raw wraps would leave the terminal echoing between polls);
|
|
213
|
-
# no-op for non-tty inputs (tests use a pipe).
|
|
214
71
|
def with_raw_input(&block)
|
|
215
72
|
if @input.stdin.tty? && @input.stdin.respond_to?(:raw)
|
|
216
73
|
@input.stdin.raw(&block)
|
|
@@ -220,13 +77,13 @@ module Letsdo
|
|
|
220
77
|
end
|
|
221
78
|
|
|
222
79
|
def install_winch_handler
|
|
223
|
-
Signal.trap(
|
|
80
|
+
Signal.trap('SIGWINCH') { @winch = true }
|
|
224
81
|
rescue ArgumentError
|
|
225
|
-
nil
|
|
82
|
+
nil
|
|
226
83
|
end
|
|
227
84
|
|
|
228
85
|
def restore_winch_handler
|
|
229
|
-
Signal.trap(
|
|
86
|
+
Signal.trap('SIGWINCH', 'DEFAULT')
|
|
230
87
|
rescue ArgumentError
|
|
231
88
|
nil
|
|
232
89
|
end
|
|
@@ -250,4 +107,4 @@ module Letsdo
|
|
|
250
107
|
end
|
|
251
108
|
end
|
|
252
109
|
end
|
|
253
|
-
end
|
|
110
|
+
end
|
data/lib/letsdo/tui/terminal.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'tty-cursor'
|
|
4
4
|
|
|
5
5
|
module Letsdo
|
|
6
6
|
module Tui
|
|
@@ -24,11 +24,14 @@ module Letsdo
|
|
|
24
24
|
# @param size_provider [Proc] callable → [height, width]; defaults to
|
|
25
25
|
# TTY::Screen.size; injected in tests for deterministic size
|
|
26
26
|
def initialize(stream:, size_provider: nil)
|
|
27
|
-
require
|
|
27
|
+
require 'tty-screen' unless defined?(TTY::Screen)
|
|
28
28
|
|
|
29
29
|
@stream = stream
|
|
30
30
|
@size_provider = size_provider ||
|
|
31
|
-
|
|
31
|
+
lambda {
|
|
32
|
+
size = TTY::Screen.size
|
|
33
|
+
[size[0] || 24, size[1] || 80]
|
|
34
|
+
}
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
# Enters the alternate screen and hides the cursor.
|
|
@@ -64,4 +67,4 @@ module Letsdo
|
|
|
64
67
|
end
|
|
65
68
|
end
|
|
66
69
|
end
|
|
67
|
-
end
|
|
70
|
+
end
|
data/lib/letsdo/tui.rb
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
# Letsdo::Tui::Input - tty-reader key decoding (injectable)
|
|
17
17
|
# Letsdo::Tui::Session - controller: terminal lifecycle + input thread
|
|
18
18
|
|
|
19
|
-
require_relative
|
|
20
|
-
require_relative
|
|
21
|
-
require_relative
|
|
22
|
-
require_relative
|
|
23
|
-
require_relative
|
|
24
|
-
require_relative
|
|
19
|
+
require_relative 'tui/log_buffer'
|
|
20
|
+
require_relative 'tui/metrics'
|
|
21
|
+
require_relative 'tui/renderer'
|
|
22
|
+
require_relative 'tui/terminal'
|
|
23
|
+
require_relative 'tui/input'
|
|
24
|
+
require_relative 'tui/session'
|
data/lib/letsdo/version.rb
CHANGED
data/lib/letsdo.rb
CHANGED
|
@@ -3,28 +3,34 @@
|
|
|
3
3
|
# The Letsdo package — a local agent worker for Backlog.md/markdown tasks.
|
|
4
4
|
#
|
|
5
5
|
# OOP structure:
|
|
6
|
-
# Letsdo::Errors - error hierarchy (
|
|
6
|
+
# Letsdo::Errors - error hierarchy (Letsdo::Error and others)
|
|
7
7
|
# Letsdo::PromptStore - access to agents/*.md prompts
|
|
8
|
+
# Letsdo::DefaultPrompt - the built-in default prompt (fallback run, --init template)
|
|
8
9
|
# Letsdo::OutputStreamer - where and how agent text and service lines are printed
|
|
9
10
|
# Letsdo::PiRunner - running pi --mode json and parsing the event stream
|
|
10
|
-
# Letsdo::Agent - a single agent run: prompt from agents/ + pi
|
|
11
|
+
# Letsdo::Agent - a single agent run: prompt from agents/ or default + pi
|
|
12
|
+
# Letsdo::Capture - interruption-safe child stdout/stderr capture
|
|
11
13
|
# Letsdo::BacklogTasks - open tasks from the backlog CLI (the task provider)
|
|
12
14
|
# Letsdo::Loop - generic orchestrator: tasks → runs → waiting
|
|
13
15
|
# Letsdo::AgentLoop - letsdo wiring: provider + agent + signals + messages
|
|
16
|
+
# Letsdo::Control - agent-control primitives (PauseGate)
|
|
14
17
|
# Letsdo::Tui - the interactive TUI (LogBuffer, Metrics,
|
|
15
18
|
# Renderer, Terminal, Input, Session)
|
|
16
19
|
# Letsdo::CLI - command-line arguments, usage, exit code
|
|
17
20
|
#
|
|
18
21
|
# Entry point — bin/letsdo (a thin wrapper over Letsdo::CLI).
|
|
19
22
|
|
|
20
|
-
require_relative
|
|
21
|
-
require_relative
|
|
22
|
-
require_relative
|
|
23
|
-
require_relative
|
|
24
|
-
require_relative
|
|
25
|
-
require_relative
|
|
26
|
-
require_relative
|
|
27
|
-
require_relative
|
|
28
|
-
require_relative
|
|
29
|
-
require_relative
|
|
30
|
-
require_relative
|
|
23
|
+
require_relative 'letsdo/version'
|
|
24
|
+
require_relative 'letsdo/errors'
|
|
25
|
+
require_relative 'letsdo/prompt_store'
|
|
26
|
+
require_relative 'letsdo/default_prompt'
|
|
27
|
+
require_relative 'letsdo/output_streamer'
|
|
28
|
+
require_relative 'letsdo/pi_runner'
|
|
29
|
+
require_relative 'letsdo/agent'
|
|
30
|
+
require_relative 'letsdo/capture'
|
|
31
|
+
require_relative 'letsdo/backlog_tasks'
|
|
32
|
+
require_relative 'letsdo/loop'
|
|
33
|
+
require_relative 'letsdo/agent_loop'
|
|
34
|
+
require_relative 'letsdo/control'
|
|
35
|
+
require_relative 'letsdo/tui'
|
|
36
|
+
require_relative 'letsdo/cli'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: letsdo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergei O. Udalov
|
|
@@ -65,6 +65,20 @@ dependencies:
|
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '2.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.77.0
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.77.0
|
|
68
82
|
description: 'letsdo — a local agent worker for Backlog.md/markdown tasks. OOP structure:
|
|
69
83
|
Letsdo::PromptStore (agents/), Letsdo::OutputStreamer and Letsdo::PiRunner (pi --mode
|
|
70
84
|
json), Letsdo::Agent (one run), Letsdo::Loop (orchestrator loop), Letsdo::CLI. Minitest
|
|
@@ -82,12 +96,21 @@ files:
|
|
|
82
96
|
- lib/letsdo.rb
|
|
83
97
|
- lib/letsdo/agent.rb
|
|
84
98
|
- lib/letsdo/agent_loop.rb
|
|
99
|
+
- lib/letsdo/agent_loop/tasks.rb
|
|
85
100
|
- lib/letsdo/backlog_tasks.rb
|
|
101
|
+
- lib/letsdo/capture.rb
|
|
86
102
|
- lib/letsdo/cli.rb
|
|
103
|
+
- lib/letsdo/cli/init.rb
|
|
104
|
+
- lib/letsdo/cli/launch.rb
|
|
105
|
+
- lib/letsdo/control.rb
|
|
106
|
+
- lib/letsdo/default_prompt.rb
|
|
87
107
|
- lib/letsdo/errors.rb
|
|
88
108
|
- lib/letsdo/loop.rb
|
|
89
109
|
- lib/letsdo/output_streamer.rb
|
|
110
|
+
- lib/letsdo/output_streamer/arg_summary.rb
|
|
90
111
|
- lib/letsdo/pi_runner.rb
|
|
112
|
+
- lib/letsdo/pi_runner/events.rb
|
|
113
|
+
- lib/letsdo/pi_runner/process.rb
|
|
91
114
|
- lib/letsdo/prompt_store.rb
|
|
92
115
|
- lib/letsdo/tui.rb
|
|
93
116
|
- lib/letsdo/tui/input.rb
|
|
@@ -95,12 +118,19 @@ files:
|
|
|
95
118
|
- lib/letsdo/tui/metrics.rb
|
|
96
119
|
- lib/letsdo/tui/renderer.rb
|
|
97
120
|
- lib/letsdo/tui/session.rb
|
|
121
|
+
- lib/letsdo/tui/session/keys.rb
|
|
122
|
+
- lib/letsdo/tui/session/view.rb
|
|
98
123
|
- lib/letsdo/tui/terminal.rb
|
|
99
124
|
- lib/letsdo/version.rb
|
|
125
|
+
homepage: https://github.com/sergio-fry/letsdo
|
|
100
126
|
licenses:
|
|
101
127
|
- MIT
|
|
102
128
|
metadata:
|
|
129
|
+
homepage_uri: https://github.com/sergio-fry/letsdo
|
|
130
|
+
source_code_uri: https://github.com/sergio-fry/letsdo
|
|
131
|
+
changelog_uri: https://github.com/sergio-fry/letsdo/blob/main/CHANGELOG.md
|
|
103
132
|
rubygems_mfa_required: 'true'
|
|
133
|
+
allowed_push_host: https://rubygems.org
|
|
104
134
|
rdoc_options: []
|
|
105
135
|
require_paths:
|
|
106
136
|
- lib
|