flycal-cli 1.0 → 1.2
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/config/defaults.yml +3 -28
- data/config/oauth_client.json +14 -0
- data/lib/flycal/cli/app.rb +112 -143
- data/lib/flycal/cli/auth.rb +113 -42
- data/lib/flycal/cli/config.rb +24 -1
- data/lib/flycal/client.rb +73 -98
- data/lib/flycal/date_time_parser.rb +11 -10
- data/lib/flycal/default_slots.rb +77 -0
- data/lib/flycal/mock/event_generator.rb +6 -4
- data/lib/flycal/mock/slot_scenario.rb +78 -0
- data/lib/flycal/pipeline/json_renderer.rb +1 -5
- data/lib/flycal/pipeline/text_renderer.rb +5 -4
- data/lib/flycal/slot_finder.rb +16 -16
- data/lib/flycal/slot_formatter.rb +8 -4
- data/lib/flycal/slot_query.rb +80 -0
- data/lib/flycal/slot_templates.rb +129 -0
- data/lib/flycal/time_format.rb +114 -0
- data/lib/flycal.rb +1 -1
- data/lib/flycal_cli.rb +5 -0
- data/locales/en.json +18 -0
- data/locales/it.json +18 -0
- data/mcp/tools.json +15 -3
- metadata +7 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flycal
|
|
4
|
+
# Named and custom slot windows. YAML/JSON shape matches ~/.flycal/config.yml:
|
|
5
|
+
#
|
|
6
|
+
# days: [1, 2, 3, 4, 5]
|
|
7
|
+
# hours:
|
|
8
|
+
# - 9:30-13:00
|
|
9
|
+
# - 14:00-18:30
|
|
10
|
+
module SlotTemplates
|
|
11
|
+
BUILTIN = {
|
|
12
|
+
"work" => {
|
|
13
|
+
"days" => [ 1, 2, 3, 4, 5 ],
|
|
14
|
+
"hours" => [ "9:30-13:00", "14:00-18:30" ]
|
|
15
|
+
},
|
|
16
|
+
"dinner" => {
|
|
17
|
+
"days" => [ 1, 2, 3, 4, 5, 6 ],
|
|
18
|
+
"hours" => [ "19-23" ]
|
|
19
|
+
}
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
def names(catalog = nil)
|
|
25
|
+
normalize_catalog(catalog || DefaultSlots.templates).keys
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Resolve a named template, or a custom YAML-shaped hash.
|
|
29
|
+
# +custom+ wins over +name+ when present.
|
|
30
|
+
def resolve(name: nil, custom: nil, catalog: nil)
|
|
31
|
+
if custom_present?(custom)
|
|
32
|
+
parsed = parse(custom)
|
|
33
|
+
return parsed.merge(name: "custom")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
source = normalize_catalog(catalog || DefaultSlots.templates)
|
|
37
|
+
raise Error, "slots.templates is missing." if source.empty?
|
|
38
|
+
key = name.to_s.strip
|
|
39
|
+
key = source.keys.first.to_s if key.empty?
|
|
40
|
+
definition = source[key]
|
|
41
|
+
unless definition
|
|
42
|
+
raise Error, "Unknown slots template #{key.inspect}. Available: #{source.keys.join(", ")}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
parse(definition).merge(name: key)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def parse(definition)
|
|
49
|
+
data = indifferent(definition)
|
|
50
|
+
days = parse_days(data[:days])
|
|
51
|
+
hours = parse_hours(data[:hours])
|
|
52
|
+
{ days: days, hours: hours }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def parse_days(values)
|
|
56
|
+
days = Array(values).map(&:to_i)
|
|
57
|
+
raise Error, "slots template days cannot be empty." if days.empty?
|
|
58
|
+
|
|
59
|
+
invalid = days.reject { |d| d.between?(1, 7) }
|
|
60
|
+
raise Error, "Invalid template days #{invalid.inspect}. Use 1 (Mon) .. 7 (Sun)." unless invalid.empty?
|
|
61
|
+
|
|
62
|
+
days.uniq
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def parse_hours(values)
|
|
66
|
+
return values if values.is_a?(Array) && values.first.is_a?(Array)
|
|
67
|
+
|
|
68
|
+
ranges = Array(values).map { |item| parse_hour_range(item) }
|
|
69
|
+
raise Error, "template hours cannot be empty." if ranges.empty?
|
|
70
|
+
|
|
71
|
+
ranges.sort_by { |sh, sm, _eh, _em| (sh * 60) + sm }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def parse_hour_range(item)
|
|
75
|
+
start_str, end_str = item.to_s.strip.split("-", 2)
|
|
76
|
+
if start_str.nil? || end_str.nil? || start_str.empty? || end_str.empty?
|
|
77
|
+
raise Error, "Invalid hours item #{item.inspect}. Use format like '9-13' or '14:30-18:00'."
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
sh, sm = parse_clock(start_str)
|
|
81
|
+
eh, em = parse_clock(end_str)
|
|
82
|
+
start_minutes = (sh * 60) + sm
|
|
83
|
+
end_minutes = (eh * 60) + em
|
|
84
|
+
raise Error, "Invalid hours range #{item.inspect}: end must be after start." if end_minutes <= start_minutes
|
|
85
|
+
|
|
86
|
+
[ sh, sm, eh, em ]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def parse_clock(value)
|
|
90
|
+
str = value.to_s.strip
|
|
91
|
+
if str.match?(/\A\d{1,2}\z/)
|
|
92
|
+
hour = str.to_i
|
|
93
|
+
raise Error, "Invalid hour #{value.inspect}. Must be 0-23." unless hour.between?(0, 23)
|
|
94
|
+
|
|
95
|
+
return [ hour, 0 ]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
hour_s, minute_s = str.split(":", 2)
|
|
99
|
+
hour = hour_s.to_i
|
|
100
|
+
minute = minute_s.to_i
|
|
101
|
+
unless hour.between?(0, 23) && minute.between?(0, 59)
|
|
102
|
+
raise Error, "Invalid time #{value.inspect}. Use HH:MM (e.g. 9:00, 18:30)."
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
[ hour, minute ]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def custom_present?(custom)
|
|
109
|
+
return false if custom.nil?
|
|
110
|
+
return false if custom.respond_to?(:blank?) && custom.blank?
|
|
111
|
+
return false if custom.respond_to?(:empty?) && custom.empty?
|
|
112
|
+
|
|
113
|
+
true
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def normalize_catalog(catalog)
|
|
117
|
+
indifferent(catalog).each_with_object({}) do |(key, value), out|
|
|
118
|
+
out[key.to_s] = value
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def indifferent(value)
|
|
123
|
+
return {} if value.nil?
|
|
124
|
+
return value.with_indifferent_access if value.respond_to?(:with_indifferent_access)
|
|
125
|
+
|
|
126
|
+
value
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
require "date"
|
|
5
|
+
require "active_support/time"
|
|
6
|
+
|
|
7
|
+
module Flycal
|
|
8
|
+
# Named-zone clock for template hours and every datetime Flycal emits.
|
|
9
|
+
#
|
|
10
|
+
# Wall-clock windows (work 9:30–13, dinner 19–23, template_custom) are local
|
|
11
|
+
# to this zone — never the server process TZ (often UTC in production).
|
|
12
|
+
# JSON start/end/from/to always share that same offset.
|
|
13
|
+
module TimeFormat
|
|
14
|
+
DEFAULT_ZONE = "Europe/Rome"
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def with_zone(name)
|
|
19
|
+
previous = Thread.current[:flycal_timezone]
|
|
20
|
+
Thread.current[:flycal_timezone] = coerce_zone(name)
|
|
21
|
+
yield current_zone
|
|
22
|
+
ensure
|
|
23
|
+
Thread.current[:flycal_timezone] = previous
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def current_zone
|
|
27
|
+
Thread.current[:flycal_timezone] || coerce_zone(DEFAULT_ZONE)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def coerce_zone(name)
|
|
31
|
+
return name if name.is_a?(ActiveSupport::TimeZone)
|
|
32
|
+
|
|
33
|
+
key = name.to_s.strip
|
|
34
|
+
key = DEFAULT_ZONE if key.empty?
|
|
35
|
+
ActiveSupport::TimeZone[key] || ActiveSupport::TimeZone[DEFAULT_ZONE]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def zone_name(name = nil)
|
|
39
|
+
zone = if name.nil? || name.to_s.strip.empty?
|
|
40
|
+
current_zone
|
|
41
|
+
else
|
|
42
|
+
coerce_zone(name)
|
|
43
|
+
end
|
|
44
|
+
zone.tzinfo&.identifier || zone.name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# explicit param → Google calendar timeZone → FLYCAL_TIMEZONE → Europe/Rome.
|
|
48
|
+
# Never uses ENV["TZ"] (Docker/Kamal set that to UTC).
|
|
49
|
+
def resolve(explicit: nil, calendars: nil, calendar_ids: nil)
|
|
50
|
+
key = explicit.to_s.strip
|
|
51
|
+
return zone_name(key) unless key.empty?
|
|
52
|
+
|
|
53
|
+
from_cal = from_calendars(calendars, calendar_ids)
|
|
54
|
+
return from_cal if from_cal
|
|
55
|
+
|
|
56
|
+
env = ENV["FLYCAL_TIMEZONE"].to_s.strip
|
|
57
|
+
return zone_name(env) unless env.empty?
|
|
58
|
+
|
|
59
|
+
DEFAULT_ZONE
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def from_calendars(calendars, calendar_ids = nil)
|
|
63
|
+
return nil if calendars.nil? || (calendars.respond_to?(:empty?) && calendars.empty?)
|
|
64
|
+
|
|
65
|
+
ids = Array(calendar_ids).map(&:to_s)
|
|
66
|
+
list = Array(calendars)
|
|
67
|
+
selected =
|
|
68
|
+
if ids.empty?
|
|
69
|
+
list
|
|
70
|
+
else
|
|
71
|
+
matched = list.select { |cal| cal.respond_to?(:id) && ids.include?(cal.id.to_s) }
|
|
72
|
+
matched.empty? ? list : matched
|
|
73
|
+
end
|
|
74
|
+
cal = selected.find { |c| c.respond_to?(:primary) && c.primary } || selected.first
|
|
75
|
+
tz = cal.respond_to?(:time_zone) ? cal.time_zone : nil
|
|
76
|
+
return nil if tz.to_s.strip.empty?
|
|
77
|
+
|
|
78
|
+
zone_name(tz)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def now
|
|
82
|
+
current_zone.now
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def today
|
|
86
|
+
current_zone.today
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def local(year, month, day, hour = 0, min = 0, sec = 0, zone: current_zone)
|
|
90
|
+
coerce_zone(zone).local(year, month, day, hour, min, sec)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def iso8601(value, zone: current_zone)
|
|
94
|
+
unified(value, zone: zone)&.iso8601
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def unified(value, zone: current_zone)
|
|
98
|
+
time = coerce(value)
|
|
99
|
+
return nil unless time
|
|
100
|
+
|
|
101
|
+
coerce_zone(zone).at(time.to_f)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def coerce(value)
|
|
105
|
+
return nil if value.nil?
|
|
106
|
+
return value if value.is_a?(Time)
|
|
107
|
+
return value.to_time if value.respond_to?(:to_time)
|
|
108
|
+
|
|
109
|
+
Time.parse(value.to_s)
|
|
110
|
+
rescue ArgumentError
|
|
111
|
+
nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/flycal.rb
CHANGED
data/lib/flycal_cli.rb
CHANGED
|
@@ -10,6 +10,7 @@ $LOAD_PATH.unshift(cli_lib) unless $LOAD_PATH.include?(cli_lib)
|
|
|
10
10
|
require "flycal/version"
|
|
11
11
|
require "flycal/error"
|
|
12
12
|
require "flycal/locale"
|
|
13
|
+
require "flycal/time_format"
|
|
13
14
|
require "flycal/credentials"
|
|
14
15
|
require "flycal/event_mapper"
|
|
15
16
|
require "flycal/cache_key"
|
|
@@ -21,11 +22,15 @@ require "flycal/description_query"
|
|
|
21
22
|
require "flycal/calendar_query"
|
|
22
23
|
require "flycal/calendar_service"
|
|
23
24
|
require "flycal/slot_finder"
|
|
25
|
+
require "flycal/slot_templates"
|
|
26
|
+
require "flycal/default_slots"
|
|
27
|
+
require "flycal/slot_query"
|
|
24
28
|
require "flycal/slot_formatter"
|
|
25
29
|
require "flycal/mock"
|
|
26
30
|
require "flycal/mock/config"
|
|
27
31
|
require "flycal/mock/event_generator"
|
|
28
32
|
require "flycal/mock/calendar_service"
|
|
33
|
+
require "flycal/mock/slot_scenario"
|
|
29
34
|
require "flycal/pipeline"
|
|
30
35
|
require "flycal/pipeline/params"
|
|
31
36
|
require "flycal/pipeline/retriever"
|
data/locales/en.json
CHANGED
|
@@ -28,6 +28,24 @@
|
|
|
28
28
|
"failed": "Error: could not open config with %{editor}."
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
"login": {
|
|
32
|
+
"prompt": "How do you want to sign in?",
|
|
33
|
+
"options": {
|
|
34
|
+
"flycal": "Google account (Flycal)",
|
|
35
|
+
"json": "Your own Google Cloud client JSON (~/.flycal/credentials.json)"
|
|
36
|
+
},
|
|
37
|
+
"already_connected": "✓ You are already connected to your Google account.",
|
|
38
|
+
"next_config": "Run 'flycal config' to set the default calendar.",
|
|
39
|
+
"success": "✓ Authentication completed successfully!",
|
|
40
|
+
"cancelled": "Login cancelled.",
|
|
41
|
+
"unknown_client": "Unknown OAuth client %{client}. Use flycal or json.",
|
|
42
|
+
"open_link": "Open this link in your browser to authenticate:",
|
|
43
|
+
"waiting": "After authentication, you will be redirected back here automatically.",
|
|
44
|
+
"browser_done_title": "Authentication complete!",
|
|
45
|
+
"browser_done_body": "You can close this window and return to the terminal.",
|
|
46
|
+
"flycal_missing": "Flycal OAuth client is missing from this install (%{path}).",
|
|
47
|
+
"json_missing": "Credentials file not found at %{path}.\nCreate a Desktop app at https://console.cloud.google.com/apis/credentials\nand save the JSON there.\nAuthorized redirect URI: %{redirect}"
|
|
48
|
+
},
|
|
31
49
|
"slots": {
|
|
32
50
|
"no_available": "No available slots found.",
|
|
33
51
|
"no_calendar": "No calendar found. Run 'flycal config' to set a default.",
|
data/locales/it.json
CHANGED
|
@@ -28,6 +28,24 @@
|
|
|
28
28
|
"failed": "Errore: impossibile aprire il config con %{editor}."
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
"login": {
|
|
32
|
+
"prompt": "Come vuoi accedere?",
|
|
33
|
+
"options": {
|
|
34
|
+
"flycal": "Account Google (Flycal)",
|
|
35
|
+
"json": "Il tuo client JSON di Google Cloud (~/.flycal/credentials.json)"
|
|
36
|
+
},
|
|
37
|
+
"already_connected": "✓ Sei già connesso al tuo account Google.",
|
|
38
|
+
"next_config": "Esegui 'flycal config' per impostare il calendario predefinito.",
|
|
39
|
+
"success": "✓ Autenticazione completata!",
|
|
40
|
+
"cancelled": "Login annullato.",
|
|
41
|
+
"unknown_client": "Client OAuth sconosciuto: %{client}. Usa flycal o json.",
|
|
42
|
+
"open_link": "Apri questo link nel browser per autenticarti:",
|
|
43
|
+
"waiting": "Dopo l'autenticazione verrai reindirizzato qui automaticamente.",
|
|
44
|
+
"browser_done_title": "Autenticazione completata!",
|
|
45
|
+
"browser_done_body": "Puoi chiudere questa finestra e tornare al terminale.",
|
|
46
|
+
"flycal_missing": "Il client OAuth Flycal manca da questa installazione (%{path}).",
|
|
47
|
+
"json_missing": "File credenziali non trovato: %{path}.\nCrea un'app Desktop su https://console.cloud.google.com/apis/credentials\ne salva il JSON lì.\nURI di reindirizzamento autorizzato: %{redirect}"
|
|
48
|
+
},
|
|
31
49
|
"slots": {
|
|
32
50
|
"no_available": "Nessuno slot disponibile.",
|
|
33
51
|
"no_calendar": "Nessun calendario trovato. Esegui 'flycal config' per impostare il predefinito.",
|
data/mcp/tools.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"path": "~/.flycal/config.yml",
|
|
83
83
|
"credentials_path": "~/.flycal/credentials.json",
|
|
84
84
|
"tokens_path": "~/.flycal/tokens.yml",
|
|
85
|
-
"credentials_note": "
|
|
85
|
+
"credentials_note": "flycal login offers two clients: (1) Flycal Desktop OAuth, no Google Cloud project needed; (2) optional ~/.flycal/credentials.json from your own Desktop app. Non-interactive default is flycal (`flycal login --client flycal`). For your own JSON: `flycal login --client json`.",
|
|
86
86
|
"keys": {
|
|
87
87
|
"calendar_default": {
|
|
88
88
|
"type": ["string", "null"],
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
{
|
|
207
207
|
"name": "flycal_login",
|
|
208
208
|
"title": "Connect Google account",
|
|
209
|
-
"description": "Start OAuth browser login.
|
|
209
|
+
"description": "Start OAuth browser login. Interactive menu: Flycal Desktop client (default) or ~/.flycal/credentials.json. Use --client flycal|json to skip the menu.",
|
|
210
210
|
"interactive": true,
|
|
211
211
|
"requires_auth": false,
|
|
212
212
|
"side_effects": ["writes_tokens"],
|
|
@@ -214,6 +214,13 @@
|
|
|
214
214
|
"argv": ["flycal", "login"]
|
|
215
215
|
},
|
|
216
216
|
"parameters": {
|
|
217
|
+
"client": {
|
|
218
|
+
"cli": ["--client"],
|
|
219
|
+
"type": "string",
|
|
220
|
+
"enum": ["flycal", "json"],
|
|
221
|
+
"required": false,
|
|
222
|
+
"description": "Skip the login menu. flycal = Flycal Desktop OAuth; json = ~/.flycal/credentials.json."
|
|
223
|
+
},
|
|
217
224
|
"locale": {
|
|
218
225
|
"cli": ["--locale"],
|
|
219
226
|
"type": "string",
|
|
@@ -226,6 +233,11 @@
|
|
|
226
233
|
"type": "object",
|
|
227
234
|
"additionalProperties": false,
|
|
228
235
|
"properties": {
|
|
236
|
+
"client": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"enum": ["flycal", "json"],
|
|
239
|
+
"description": "OAuth client (--client flycal|json)."
|
|
240
|
+
},
|
|
229
241
|
"locale": {
|
|
230
242
|
"type": "string",
|
|
231
243
|
"enum": ["en", "it"],
|
|
@@ -595,7 +607,7 @@
|
|
|
595
607
|
"execution": "Spawn the flycal binary with argv from command.argv, or build argv from argv_template + option_map for each provided parameter. Capture stdout/stderr and exit code. Return stdout as MCP text content.",
|
|
596
608
|
"parameter_building": "For each non-null input property present in option_map, append the mapped flag and value. Quote values with spaces. Always append --locale when locale is set.",
|
|
597
609
|
"interactive_tools": "Do not auto-run tools with interactive=true unless the host provides a TTY. Tell the user to run them in a terminal.",
|
|
598
|
-
"auth_errors": "If output indicates the user is not connected, suggest flycal login (
|
|
610
|
+
"auth_errors": "If output indicates the user is not connected, suggest flycal login (Flycal Desktop client by default; credentials.json only if they use their own Google Cloud project).",
|
|
599
611
|
"tool_filter": "Prefer exposing flycal_search, flycal_slots, flycal_calendars, and flycal_version to agents."
|
|
600
612
|
}
|
|
601
613
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flycal-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.2'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- flycal-cli
|
|
@@ -134,6 +134,7 @@ files:
|
|
|
134
134
|
- README.md
|
|
135
135
|
- bin/flycal
|
|
136
136
|
- config/defaults.yml
|
|
137
|
+
- config/oauth_client.json
|
|
137
138
|
- lib/flycal.rb
|
|
138
139
|
- lib/flycal/cache_annotator.rb
|
|
139
140
|
- lib/flycal/cache_key.rb
|
|
@@ -149,6 +150,7 @@ files:
|
|
|
149
150
|
- lib/flycal/client.rb
|
|
150
151
|
- lib/flycal/credentials.rb
|
|
151
152
|
- lib/flycal/date_time_parser.rb
|
|
153
|
+
- lib/flycal/default_slots.rb
|
|
152
154
|
- lib/flycal/description_query.rb
|
|
153
155
|
- lib/flycal/duration_parser.rb
|
|
154
156
|
- lib/flycal/error.rb
|
|
@@ -158,6 +160,7 @@ files:
|
|
|
158
160
|
- lib/flycal/mock/calendar_service.rb
|
|
159
161
|
- lib/flycal/mock/config.rb
|
|
160
162
|
- lib/flycal/mock/event_generator.rb
|
|
163
|
+
- lib/flycal/mock/slot_scenario.rb
|
|
161
164
|
- lib/flycal/pipeline.rb
|
|
162
165
|
- lib/flycal/pipeline/aggregator.rb
|
|
163
166
|
- lib/flycal/pipeline/json_renderer.rb
|
|
@@ -168,6 +171,9 @@ files:
|
|
|
168
171
|
- lib/flycal/pipeline/text_renderer.rb
|
|
169
172
|
- lib/flycal/slot_finder.rb
|
|
170
173
|
- lib/flycal/slot_formatter.rb
|
|
174
|
+
- lib/flycal/slot_query.rb
|
|
175
|
+
- lib/flycal/slot_templates.rb
|
|
176
|
+
- lib/flycal/time_format.rb
|
|
171
177
|
- lib/flycal/version.rb
|
|
172
178
|
- lib/flycal_cli.rb
|
|
173
179
|
- locales/en.json
|