flycal-cli 0.6.1 → 0.7.1

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: b7333fcbb8ba227c7694b96cba4b35893e324c13e38bdc6d996e69f54cc3d64a
4
- data.tar.gz: aab159e8f8e68368db1e45d1a799dd93386f79a0f2f9c29bb92aba776bcefa21
3
+ metadata.gz: 91347372adf44296db16d6e06a90a327a2b1dd3709137dc207dc4eadaab3a472
4
+ data.tar.gz: 0ec160527a68a2dee05f466626c28ee8bedd750a05928978a1f77991ae3be779
5
5
  SHA512:
6
- metadata.gz: 2036031048ebcb74e1954ff1d574051945407bdef922f161f4b1ad7e8be7cd1f3dfdec6dea2efe671502a1310ba834cee44426d837a3b7dfee4fb76fbd504117
7
- data.tar.gz: 40646b4ce7bfaa3256a35c435cdf6dcb3913aaf2a3811370219e32c510fc88d7469d652b573973016d96b9e1d5f49cca8da159bfad7d787742dc8c0bba9239d0
6
+ metadata.gz: 7b8eaa8bdc8e56fbb6f865d77811e017ab239bd800b8e507bbe3477a4513663335dc74ca9f28b3111eeff38fe37cc5aa010dea81d4339074bd38e9ec2c7cf7f0
7
+ data.tar.gz: faf3a5c3bad9ebf2b966b9ca66f1b924d619496cde86b4c8d1b3246a8af2488a12dc6a5b23cbf2f7be147e1cc09e96ed99b6aa23d7d74ab6e5ce9f2e97ed69b3
data/README.md CHANGED
@@ -157,10 +157,10 @@ Find free time slots in your calendar. Output is a simple list for copy/paste in
157
157
 
158
158
  ```bash
159
159
  flycal slots
160
+ flycal slots --in "5 days"
161
+ flycal slots --in "12 days" --template dinner
160
162
  flycal slots --duration 1h
161
163
  flycal slots --from 2026-08-01 --in "2 weeks" --duration 1h
162
- flycal slots --from 01/08/2026 --in 3days --duration 30min
163
- flycal slots --in 1week -c Work
164
164
  ```
165
165
 
166
166
  **Options:**
@@ -168,24 +168,26 @@ flycal slots --in 1week -c Work
168
168
  - `--duration` — Slot length. Default: `slots.defaults.default_duration` in config (`45min`). Examples: `1h`, `30 minutes`
169
169
  - `--from` / `-f` — Start date/time. Default: `slots.defaults.from` in config (`now`). If a date without time is today, starts from the current time.
170
170
  - `--in` / `-i` — Search window from `--from`. Default: `1 week`.
171
+ - `--template` / `-T` — Template from `slots.templates` in config. Default: first template (`work`)
171
172
  - `--calendar` / `-c` — Calendar name or ID used as fallback when `exclude_calendars` is not configured
172
173
 
173
174
  **Output:**
174
175
 
175
- Always starts with a header and clickable calendar links (OSC 8), then the availability list:
176
-
177
176
  ```
178
- found 3 slots
179
- from wed 29 July 2026 to wed 5 August 2026
180
- with duration 45min
181
- for calendars
182
- Incode - Antonio
183
- Personal
184
- https://calendar.google.com/calendar/r/day/2026/7/29
185
-
186
- friday 15/7
187
- 10-12
188
- 13-15.30
177
+ found 6 slots
178
+ from sat 1 August 2026 to sat 8 August 2026
179
+ with duration 45min, template dinner
180
+ considering calendars
181
+ - incode - antonio
182
+ - Polimi 10110009
183
+ - antoniomolinari1977@gmail.com
184
+ link: https://calendar.google.com/calendar/r/day/2026/8/1
185
+
186
+ saturday 1/8
187
+ 19 - 23
188
+
189
+ monday 3/8
190
+ 19 - 23
189
191
  ```
190
192
 
191
193
  If `pbcopy` is available (macOS), the slot list without the header is also copied to the clipboard.
@@ -193,44 +195,62 @@ If `pbcopy` is available (macOS), the slot list without the header is also copie
193
195
  Slot search windows are configured in `~/.flycal/config.yml`:
194
196
 
195
197
  ```yaml
198
+ calendar_default: ~
199
+ locale: en
196
200
  slots:
201
+ templates:
202
+ work:
203
+ days:
204
+ - 1
205
+ - 2
206
+ - 3
207
+ - 4
208
+ - 5
209
+ hours:
210
+ - 9:30-13:00
211
+ - 14:00-18:30
212
+ dinner:
213
+ days:
214
+ - 1
215
+ - 2
216
+ - 3
217
+ - 4
218
+ - 5
219
+ - 6
220
+ - 7
221
+ hours:
222
+ - 19-23
223
+ exclude_calendars: []
197
224
  defaults:
198
225
  from: now
199
226
  default_duration: 45min
200
227
  free_before: 0m
201
228
  free_after: 15m
202
- exclude_calendars:
203
- - user@example.com
204
- - user@gmail.com
205
- hours:
206
- - 9:30-13:00
207
- - 14:00-18:30
208
- weekdays_only: true
209
- locale: en
210
229
  ```
211
230
 
212
231
  Missing keys are filled from `config/defaults.yml` in the gem and saved to your `config.yml` on first read.
213
232
 
233
+ - `calendar_default: ~` means no default calendar is set (`~` is YAML null)
234
+ - `templates` — named schedules; default used is the first one (`work`)
235
+ - `templates.*.days` — weekdays as numbers: `1` Monday … `7` Sunday
236
+ - `templates.*.hours` — one or more ranges (`H-H`, `HH:MM-HH:MM`, mixed)
214
237
  - `defaults.from` — default `--from` for slots (`now` or a date string)
215
238
  - `defaults.default_duration` — default slot length when `--duration` is omitted
216
- - `free_before` — buffer before each slot; busy intervals are extended backward so no event may fall in this window before a slot
217
- - `free_after` — buffer after each slot; gaps must fit `duration + free_after`, but output shows slots of `duration` only
218
- - `exclude_calendars` lists calendars whose events block free slots; if empty or not set, events from `calendar_default` are used
219
- - `hours` accepts one or more ranges (`H-H`, `HH:MM-HH:MM`, mixed)
220
- - `weekdays_only: true` limits slots to Monday-Friday
221
- - `weekdays_only: false` includes weekends
239
+ - `free_before` — buffer before each slot
240
+ - `free_after` — buffer after each slot; gaps must fit `duration + free_after`, output shows continuous free ranges
241
+ - `exclude_calendars` calendars whose events block free slots; if empty, `calendar_default` is used
222
242
  - `locale` supports `en` and `it` (default is `en`)
223
243
 
224
244
  **Output example:**
225
245
 
226
246
  ```
227
247
  friday 15/7
228
- 10-12
229
- 13-15.30
248
+ 10 - 12
249
+ 13 - 15.30
230
250
 
231
251
  monday 21/7
232
- 12-13
233
- 14-15
252
+ 12 - 13
253
+ 14 - 15
234
254
  ```
235
255
 
236
256
  ## Configuration
data/config/defaults.yml CHANGED
@@ -1,12 +1,30 @@
1
+ calendar_default: ~
1
2
  locale: en
2
3
  slots:
4
+ templates:
5
+ work:
6
+ days:
7
+ - 1
8
+ - 2
9
+ - 3
10
+ - 4
11
+ - 5
12
+ hours:
13
+ - 9:30-13:00
14
+ - 14:00-18:30
15
+ dinner:
16
+ days:
17
+ - 1
18
+ - 2
19
+ - 3
20
+ - 4
21
+ - 5
22
+ - 6
23
+ hours:
24
+ - 19-23
25
+ exclude_calendars: []
3
26
  defaults:
4
27
  from: now
5
28
  default_duration: 45min
6
29
  free_before: 0m
7
30
  free_after: 15m
8
- exclude_calendars: []
9
- hours:
10
- - 9:30-13:00
11
- - 14:00-18:30
12
- weekdays_only: true
@@ -182,11 +182,13 @@ module FlycalCli
182
182
  --from from config (default: now)
183
183
  --in 1 week
184
184
  --duration from config (default: 45min)
185
+ --template first template in config (default: work)
185
186
 
186
187
  Examples:
187
188
  flycal slots
188
- flycal slots --duration 1h
189
- flycal slots --from 2026-08-01 --in "2 weeks" --duration 1h
189
+ flycal slots --in "5 days"
190
+ flycal slots --in "12 days" --template dinner
191
+ flycal slots --duration 1h --from 2026-08-01
190
192
  LONGDESC
191
193
  option :duration, type: :string,
192
194
  desc: "Slot length (default from config: slots.defaults.default_duration)"
@@ -194,6 +196,8 @@ module FlycalCli
194
196
  desc: "Search window from --from (default: 1 week)"
195
197
  option :from, type: :string, aliases: "-f",
196
198
  desc: "Start date/time (default from config: slots.defaults.from)"
199
+ option :template, type: :string, aliases: "-T",
200
+ desc: "Slot template name from config (default: first template, usually work)"
197
201
  option :calendar, type: :string, aliases: "-c", desc: "Calendar name or ID"
198
202
  def slots
199
203
  apply_locale_override
@@ -216,13 +220,14 @@ module FlycalCli
216
220
  from_value = "now" if from_value.empty?
217
221
 
218
222
  begin
223
+ template_name, template = resolve_slots_template(slot_cfg, options[:template])
219
224
  slot_duration = DurationParser.to_seconds(duration_value)
220
225
  free_before = DurationParser.to_seconds(slot_cfg["free_before"] || "0m")
221
226
  free_after = DurationParser.to_seconds(slot_cfg["free_after"] || "0m")
222
227
  time_min = parse_slots_from(from_value)
223
228
  time_max = DurationParser.add_to_time(in_value, time_min)
224
- workhours = parse_workhours(slot_cfg["hours"])
225
- weekdays_only = !!slot_cfg["weekdays_only"]
229
+ hours = parse_workhours(template["hours"])
230
+ days = parse_template_days(template["days"])
226
231
  rescue FlycalCli::Error => e
227
232
  puts "Error: #{e.message}"
228
233
  exit 1
@@ -266,8 +271,8 @@ module FlycalCli
266
271
  time_min: time_min,
267
272
  time_max: time_max,
268
273
  slot_duration_seconds: slot_duration,
269
- workhours: workhours,
270
- weekdays_only: weekdays_only,
274
+ hours: hours,
275
+ days: days,
271
276
  free_before_seconds: free_before,
272
277
  free_after_seconds: free_after
273
278
  )
@@ -279,7 +284,8 @@ module FlycalCli
279
284
  to: time_max,
280
285
  duration: duration_value,
281
286
  calendars: calendar_meta,
282
- count: slot_count
287
+ count: slot_count,
288
+ template: template_name
283
289
  )
284
290
  puts ""
285
291
  output = SlotFormatter.format_output(slots_by_day)
@@ -472,10 +478,36 @@ module FlycalCli
472
478
  DateTimeParser.parse(value)
473
479
  end
474
480
 
481
+ def resolve_slots_template(slot_cfg, requested_name)
482
+ templates = slot_cfg["templates"]
483
+ raise FlycalCli::Error, "slots.templates is missing in config.yml." unless templates.is_a?(Hash) && !templates.empty?
484
+
485
+ name = requested_name.to_s.strip
486
+ name = templates.keys.first.to_s if name.empty?
487
+
488
+ template = templates[name]
489
+ unless template.is_a?(Hash)
490
+ available = templates.keys.join(", ")
491
+ raise FlycalCli::Error, "Unknown slots template #{name.inspect}. Available: #{available}"
492
+ end
493
+
494
+ [name, template]
495
+ end
496
+
497
+ def parse_template_days(values)
498
+ days = Array(values).map(&:to_i)
499
+ raise FlycalCli::Error, "slots template days cannot be empty." if days.empty?
500
+
501
+ invalid = days.reject { |d| d.between?(1, 7) }
502
+ raise FlycalCli::Error, "Invalid template days #{invalid.inspect}. Use 1 (Mon) .. 7 (Sun)." unless invalid.empty?
503
+
504
+ days.uniq
505
+ end
506
+
475
507
  def copy_slots_to_clipboard(text)
476
508
  return unless pbcopy_available?
477
509
 
478
- IO.popen("pbcopy", "w") { |io| io.write(text) }
510
+ IO.popen("pbcopy", "w") { |io| io.write(SlotFormatter.strip_ansi(text)) }
479
511
  puts ""
480
512
  puts Locale.t("slots.copied_to_clipboard")
481
513
  rescue StandardError
@@ -513,7 +545,7 @@ module FlycalCli
513
545
  [sh, sm, eh, em]
514
546
  end
515
547
 
516
- raise FlycalCli::Error, "slots.hours cannot be empty in config.yml." if ranges.empty?
548
+ raise FlycalCli::Error, "template hours cannot be empty in config.yml." if ranges.empty?
517
549
 
518
550
  ranges.sort_by { |sh, sm, _eh, _em| (sh * 60) + sm }
519
551
  end
@@ -32,7 +32,10 @@ module FlycalCli
32
32
  end
33
33
 
34
34
  def calendar_default
35
- load["calendar_default"]
35
+ value = load["calendar_default"]
36
+ return nil if value.nil? || value.to_s.strip.empty? || value.to_s == "~"
37
+
38
+ value.to_s
36
39
  end
37
40
 
38
41
  def calendar_default=(calendar_id)
@@ -98,6 +101,38 @@ module FlycalCli
98
101
  changed = true
99
102
  end
100
103
 
104
+ if normalized.key?("tempaltes") && !normalized.key?("templates")
105
+ normalized["templates"] = normalized.delete("tempaltes")
106
+ changed = true
107
+ end
108
+
109
+ templates = normalized["templates"]
110
+ templates_empty = !templates.is_a?(Hash) || templates.empty?
111
+
112
+ if templates_empty && normalized["hours"]
113
+ days =
114
+ if normalized.key?("weekdays_only") && normalized["weekdays_only"] == false
115
+ [1, 2, 3, 4, 5, 6, 7]
116
+ else
117
+ [1, 2, 3, 4, 5]
118
+ end
119
+
120
+ normalized["templates"] = {
121
+ "work" => {
122
+ "days" => days,
123
+ "hours" => deep_dup(normalized["hours"])
124
+ }
125
+ }
126
+ changed = true
127
+ end
128
+
129
+ %w[hours weekdays_only weekdays-only workhours tempaltes].each do |legacy_key|
130
+ next unless normalized.key?(legacy_key)
131
+
132
+ normalized.delete(legacy_key)
133
+ changed = true
134
+ end
135
+
101
136
  return [data, false] unless changed
102
137
 
103
138
  data = data.dup
@@ -2,7 +2,8 @@
2
2
 
3
3
  module FlycalCli
4
4
  class SlotFinder
5
- DEFAULT_WORKHOURS = [[9, 0, 18, 0]].freeze
5
+ DEFAULT_HOURS = [[9, 0, 18, 0]].freeze
6
+ DEFAULT_DAYS = [1, 2, 3, 4, 5].freeze
6
7
  STEP_SECONDS = 900
7
8
 
8
9
  def initialize(
@@ -10,8 +11,8 @@ module FlycalCli
10
11
  time_min:,
11
12
  time_max:,
12
13
  slot_duration_seconds:,
13
- workhours: DEFAULT_WORKHOURS,
14
- weekdays_only: true,
14
+ hours: DEFAULT_HOURS,
15
+ days: DEFAULT_DAYS,
15
16
  free_before_seconds: 0,
16
17
  free_after_seconds: 0
17
18
  )
@@ -19,8 +20,8 @@ module FlycalCli
19
20
  @time_min = time_min
20
21
  @time_max = time_max
21
22
  @slot_duration_seconds = slot_duration_seconds
22
- @workhours = workhours
23
- @weekdays_only = weekdays_only
23
+ @hours = hours
24
+ @days = Array(days).map(&:to_i)
24
25
  @free_before_seconds = free_before_seconds
25
26
  @free_after_seconds = free_after_seconds
26
27
  end
@@ -28,7 +29,7 @@ module FlycalCli
28
29
  def slots_by_day
29
30
  result = {}
30
31
 
31
- each_workday do |date|
32
+ each_template_day do |date|
32
33
  slots = []
33
34
  day_intervals(date).each do |day_start, day_end|
34
35
  next if day_end <= day_start
@@ -47,7 +48,7 @@ module FlycalCli
47
48
 
48
49
  private
49
50
 
50
- def each_workday
51
+ def each_template_day
51
52
  date = @time_min.to_date
52
53
  end_date = @time_max.to_date
53
54
 
@@ -57,18 +58,17 @@ module FlycalCli
57
58
  end
58
59
  end
59
60
 
61
+ # Template days: 1=Monday ... 7=Sunday
60
62
  def include_day?(date)
61
- return true unless @weekdays_only
62
-
63
- workday?(date)
63
+ @days.include?(iso_weekday(date))
64
64
  end
65
65
 
66
- def workday?(date)
67
- !date.saturday? && !date.sunday?
66
+ def iso_weekday(date)
67
+ date.wday.zero? ? 7 : date.wday
68
68
  end
69
69
 
70
70
  def day_intervals(date)
71
- @workhours.map do |start_h, start_m, end_h, end_m|
71
+ @hours.map do |start_h, start_m, end_h, end_m|
72
72
  start_at = Time.local(date.year, date.month, date.day, start_h, start_m, 0)
73
73
  end_at = Time.local(date.year, date.month, date.day, end_h, end_m, 0)
74
74
  start_at = [@time_min, start_at].max
@@ -147,7 +147,6 @@ module FlycalCli
147
147
  return [] if rounded_start >= rounded_end
148
148
  return [] if (rounded_end - rounded_start) < @slot_duration_seconds
149
149
 
150
- # One continuous free range (not sliding windows of fixed duration)
151
150
  [[rounded_start, rounded_end]]
152
151
  end
153
152
 
@@ -3,19 +3,20 @@
3
3
  module FlycalCli
4
4
  class SlotFormatter
5
5
  class << self
6
- def format_header(from:, to:, duration:, calendars:, count:)
6
+ def format_header(from:, to:, duration:, calendars:, count:, template: nil)
7
7
  lines = []
8
8
  lines << Locale.t(
9
9
  "slots.header",
10
- count: count,
10
+ count: underline(count),
11
11
  from: underline(Locale.format_long_date(from)),
12
12
  to: underline(Locale.format_long_date(to)),
13
- duration: underline(duration)
13
+ duration: underline(duration),
14
+ template: underline(template.to_s)
14
15
  )
15
16
  calendars.each do |cal|
16
- lines << cal[:name].to_s
17
+ lines << "- #{cal[:name]}"
17
18
  end
18
- lines << google_calendar_day_url(from)
19
+ lines << "link: #{google_calendar_day_url(from)}"
19
20
  lines.join("\n")
20
21
  end
21
22
 
@@ -24,7 +25,7 @@ module FlycalCli
24
25
 
25
26
  slots_by_day.sort_by(&:first).each do |date, slots|
26
27
  lines << "" unless lines.empty?
27
- lines << day_header(date)
28
+ lines << underline(day_header(date))
28
29
  slots.each do |start_at, end_at|
29
30
  lines << slot_range(start_at, end_at)
30
31
  end
@@ -33,6 +34,10 @@ module FlycalCli
33
34
  lines.join("\n")
34
35
  end
35
36
 
37
+ def strip_ansi(text)
38
+ text.to_s.gsub(/\e\[[0-9;]*m/, "")
39
+ end
40
+
36
41
  private
37
42
 
38
43
  def underline(str)
@@ -49,7 +54,7 @@ module FlycalCli
49
54
  end
50
55
 
51
56
  def slot_range(start_at, end_at)
52
- "#{format_time(start_at)}-#{format_time(end_at)}"
57
+ "#{format_time(start_at)} - #{format_time(end_at)}"
53
58
  end
54
59
 
55
60
  def format_time(time)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlycalCli
4
- VERSION = '0.6.1'
4
+ VERSION = '0.7.1'
5
5
  end
data/locales/en.json CHANGED
@@ -31,7 +31,7 @@
31
31
  "slots": {
32
32
  "no_available": "No available slots found.",
33
33
  "no_calendar": "No calendar found. Run 'flycal config' to set a default.",
34
- "header": "found %{count} slots\nfrom %{from} to %{to}\nwith duration %{duration}\nfor calendars",
34
+ "header": "found %{count} slots\nfrom %{from} to %{to}\nwith duration %{duration}, template %{template}\nconsidering calendars",
35
35
  "copied_to_clipboard": "✓ Slot list copied to clipboard (pbcopy)"
36
36
  },
37
37
  "errors": {
data/locales/it.json CHANGED
@@ -31,7 +31,7 @@
31
31
  "slots": {
32
32
  "no_available": "Nessuno slot disponibile.",
33
33
  "no_calendar": "Nessun calendario trovato. Esegui 'flycal config' per impostare il predefinito.",
34
- "header": "trovati %{count} slot\nda %{from} a %{to}\ncon durata %{duration}\nper i calendari",
34
+ "header": "trovati %{count} slot\nda %{from} a %{to}\ncon durata %{duration}, template %{template}\nconsiderando i calendari",
35
35
  "copied_to_clipboard": "✓ Lista slot copiata negli appunti (pbcopy)"
36
36
  },
37
37
  "errors": {
data/mcp/tools.json CHANGED
@@ -17,18 +17,17 @@
17
17
  "credentials_path": "~/.flycal/credentials.json",
18
18
  "keys": {
19
19
  "calendar_default": {
20
- "type": "string",
21
- "description": "Default Google Calendar ID used by search and as slots fallback"
20
+ "type": ["string", "null"],
21
+ "description": "Default Google Calendar ID used by search and as slots fallback. YAML null (~) means unset."
22
22
  },
23
23
  "locale": {
24
24
  "type": "string",
25
25
  "enum": ["en", "it"],
26
26
  "description": "Default CLI locale"
27
27
  },
28
- "slots.hours": {
29
- "type": "array",
30
- "items": { "type": "string" },
31
- "description": "Slot search hour ranges, e.g. [\"9:30-13:00\", \"14:00-18:30\"]"
28
+ "slots.templates": {
29
+ "type": "object",
30
+ "description": "Named templates with days (1=Mon..7=Sun) and hours ranges. Default template is the first key (work)."
32
31
  },
33
32
  "slots.defaults.from": {
34
33
  "type": "string",
@@ -46,10 +45,6 @@
46
45
  "type": "string",
47
46
  "description": "Buffer after each slot; gaps must fit duration + free_after"
48
47
  },
49
- "slots.weekdays_only": {
50
- "type": "boolean",
51
- "description": "When true, slots are searched Mon–Fri only"
52
- },
53
48
  "slots.exclude_calendars": {
54
49
  "type": "array",
55
50
  "items": { "type": "string" },
@@ -256,7 +251,7 @@
256
251
  {
257
252
  "name": "flycal_slots",
258
253
  "title": "Find free time slots",
259
- "description": "Find free slots within configured hours, subtracting busy events from slots.exclude_calendars (or calendar_default if that list is empty). Starts from --from (default: config slots.defaults.from or now) for a window of --in (default: 1 week). Duration defaults to slots.defaults.default_duration (45min). Respects free_before and free_after buffers from config. Output begins with a multi-line header, calendar names, a Google Calendar day link, then continuous free ranges (each at least as long as duration). Read-only.",
254
+ "description": "Find free slots using a config template (days + hours). Default template is the first one (work). Use --template dinner for evening hours. Subtracts busy events from slots.exclude_calendars. Starts from --from (default: now) for --in (default: 1 week). Duration defaults to 45min. Respects free_before/free_after. Output: header, template name, calendars, Google Calendar day link, continuous free ranges.",
260
255
  "interactive": false,
261
256
  "requires_auth": true,
262
257
  "command": {
@@ -266,18 +261,20 @@
266
261
  "duration": ["--duration", "{value}"],
267
262
  "from": ["--from", "{value}"],
268
263
  "in": ["--in", "{value}"],
264
+ "template": ["--template", "{value}"],
269
265
  "calendar": ["--calendar", "{value}"],
270
266
  "locale": ["--locale", "{value}"]
271
267
  },
272
268
  "examples": [
273
269
  ["flycal", "slots"],
274
- ["flycal", "slots", "--duration", "1h"],
275
- ["flycal", "slots", "--from", "2026-08-01", "--in", "2 weeks", "--duration", "1h"]
270
+ ["flycal", "slots", "--in", "5 days"],
271
+ ["flycal", "slots", "--in", "12 days", "--template", "dinner"],
272
+ ["flycal", "slots", "--duration", "1h"]
276
273
  ]
277
274
  },
278
275
  "output": {
279
276
  "format": "text/plain",
280
- "example": "found 3 slots\nfrom wed 29 July 2026 to wed 5 August 2026\nwith duration 45min\nfor calendars\nWork\nhttps://calendar.google.com/calendar/r/day/2026/7/29\n\nfriday 15/7\n10-12\n13-15.30"
277
+ "example": "found 6 slots\nfrom sat 1 August 2026 to sat 8 August 2026\nwith duration 45min, template dinner\nconsidering calendars\n- Work\nlink: https://calendar.google.com/calendar/r/day/2026/8/1\n\nsaturday 1/8\n19 - 23"
281
278
  },
282
279
  "inputSchema": {
283
280
  "type": "object",
@@ -296,6 +293,10 @@
296
293
  "type": "string",
297
294
  "description": "Search window from --from. Default: 1 week. Examples: 3 days, 1 week, 48 hours (quote if spaces)."
298
295
  },
296
+ "template": {
297
+ "type": "string",
298
+ "description": "Template name from slots.templates (default: first template, usually work)."
299
+ },
299
300
  "calendar": {
300
301
  "type": "string",
301
302
  "description": "Fallback calendar name/ID when exclude_calendars is not configured."
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: 0.6.1
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - flycal-cli