flycal-cli 0.7.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91347372adf44296db16d6e06a90a327a2b1dd3709137dc207dc4eadaab3a472
4
- data.tar.gz: 0ec160527a68a2dee05f466626c28ee8bedd750a05928978a1f77991ae3be779
3
+ metadata.gz: 371b44577d9ce6befe5b01bc54768999844615943b982a6e9cd9cd8c9737d007
4
+ data.tar.gz: e3c6c00e03e38eccaea97ca045582a35d737916f1203963cc2c989ebf75de46b
5
5
  SHA512:
6
- metadata.gz: 7b8eaa8bdc8e56fbb6f865d77811e017ab239bd800b8e507bbe3477a4513663335dc74ca9f28b3111eeff38fe37cc5aa010dea81d4339074bd38e9ec2c7cf7f0
7
- data.tar.gz: faf3a5c3bad9ebf2b966b9ca66f1b924d619496cde86b4c8d1b3246a8af2488a12dc6a5b23cbf2f7be147e1cc09e96ed99b6aa23d7d74ab6e5ce9f2e97ed69b3
6
+ metadata.gz: 35d47be3955556a98d3301782c2fc7deb7064134cb38792f128ade6c78807d5f28131696d14b3f2a72c7d7ae5acf9764b880d634ac52665accc6aa239c1026fe
7
+ data.tar.gz: da10a2548c8bdd03ebc94e990b58e7a3d30de98e2f759a927e777e08d8617006713abeafd9e220fac4fbbb6d9c1e0835a8e0cb8650f4ae30f4a00a355bb1f478
data/README.md CHANGED
@@ -157,16 +157,18 @@ 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"
160
+ flycal slots --from monday --in "5 days"
161
+ flycal slots --from "next monday" --in "5 days" --locale it
162
+ flycal slots --from lunedi --in "5 days" --locale it
161
163
  flycal slots --in "12 days" --template dinner
162
- flycal slots --duration 1h
163
- flycal slots --from 2026-08-01 --in "2 weeks" --duration 1h
164
164
  ```
165
165
 
166
166
  **Options:**
167
167
 
168
168
  - `--duration` — Slot length. Default: `slots.defaults.default_duration` in config (`45min`). Examples: `1h`, `30 minutes`
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.
169
+ - `--from` / `-f` — Start date/time. Default: `slots.defaults.from` in config (`now`). Accepts:
170
+ - absolute: `YYYY-MM-DD`, locale dates (`DD-MM-YYYY` / `MM-DD-YYYY`), optional time
171
+ - relative: `now`, `today`/`oggi`, `tomorrow`/`domani`, `monday`/`lunedi`, `next monday`/`prossimo lunedi`, `last friday`/`scorso venerdi`
170
172
  - `--in` / `-i` — Search window from `--from`. Default: `1 week`.
171
173
  - `--template` / `-T` — Template from `slots.templates` in config. Default: first template (`work`)
172
174
  - `--calendar` / `-c` — Calendar name or ID used as fallback when `exclude_calendars` is not configured
@@ -195,7 +195,7 @@ module FlycalCli
195
195
  option :in, type: :string, aliases: "-i", default: "1 week",
196
196
  desc: "Search window from --from (default: 1 week)"
197
197
  option :from, type: :string, aliases: "-f",
198
- desc: "Start date/time (default from config: slots.defaults.from)"
198
+ desc: "Start (default: now). Dates, or relative: monday, next monday, tomorrow, lunedi..."
199
199
  option :template, type: :string, aliases: "-T",
200
200
  desc: "Slot template name from config (default: first template, usually work)"
201
201
  option :calendar, type: :string, aliases: "-c", desc: "Calendar name or ID"
@@ -12,16 +12,38 @@ module FlycalCli
12
12
  # YYYY-MM-DD, YYYY/MM/DD
13
13
  # YYYY-MM-DDTHH:MM, YYYY-MM-DD HH:MM(:SS)
14
14
  #
15
+ # Relative forms (en + it):
16
+ # now, today/oggi, tomorrow/domani, yesterday/ieri
17
+ # monday / lunedi, next monday / prossimo lunedi, last friday / scorso venerdi
18
+ #
15
19
  # With locale +it+: DD-MM-YYYY / DD/MM/YYYY (+ optional time)
16
20
  # With locale +en+ (default): MM-DD-YYYY / MM/DD/YYYY (+ optional time)
17
21
  class DateTimeParser
18
22
  TIME_SUFFIX = '(?:\s+|T)(\d{1,2}):(\d{2})(?::(\d{2}))?'.freeze
19
23
 
24
+ # Ruby Date#wday: 0=Sunday ... 6=Saturday
25
+ WEEKDAYS = {
26
+ "sunday" => 0, "sun" => 0, "domenica" => 0, "dom" => 0,
27
+ "monday" => 1, "mon" => 1, "lunedi" => 1, "lunedì" => 1, "lun" => 1,
28
+ "tuesday" => 2, "tue" => 2, "martedi" => 2, "martedì" => 2, "mar" => 2,
29
+ "wednesday" => 3, "wed" => 3, "mercoledi" => 3, "mercoledì" => 3, "mer" => 3,
30
+ "thursday" => 4, "thu" => 4, "giovedi" => 4, "giovedì" => 4, "gio" => 4,
31
+ "friday" => 5, "fri" => 5, "venerdi" => 5, "venerdì" => 5, "ven" => 5,
32
+ "saturday" => 6, "sat" => 6, "sabato" => 6, "sab" => 6
33
+ }.freeze
34
+
35
+ NEXT_WORDS = %w[next prossimo prossima].freeze
36
+ LAST_WORDS = %w[last scorso scorsa].freeze
37
+ THIS_WORDS = %w[this questo questa].freeze
38
+
20
39
  class << self
21
40
  def parse(str, end_of_day: false, locale: nil)
22
41
  value = str.to_s.strip
23
42
  raise FlycalCli::Error, invalid_message(str) if value.empty?
24
43
 
44
+ relative = parse_relative(value, end_of_day: end_of_day)
45
+ return relative if relative
46
+
25
47
  loc = (locale || Locale.current_locale).to_s
26
48
  formats = formats_for(loc)
27
49
 
@@ -33,7 +55,6 @@ module FlycalCli
33
55
  return build_time(year, month, day, hour, min, sec, end_of_day: end_of_day)
34
56
  end
35
57
 
36
- # Last resort: ISO8601 / Time.parse for full timestamps
37
58
  begin
38
59
  return Time.iso8601(value)
39
60
  rescue ArgumentError
@@ -56,6 +77,76 @@ module FlycalCli
56
77
 
57
78
  private
58
79
 
80
+ def parse_relative(value, end_of_day:)
81
+ normalized = value.downcase.strip
82
+ normalized = normalized.tr("àèéìòù", "aeeiou")
83
+
84
+ case normalized
85
+ when "now", "adesso", "ora"
86
+ return Time.now
87
+ when "today", "oggi"
88
+ return time_for_date(Date.today, end_of_day: end_of_day)
89
+ when "tomorrow", "domani"
90
+ return time_for_date(Date.today + 1, end_of_day: end_of_day)
91
+ when "yesterday", "ieri"
92
+ return time_for_date(Date.today - 1, end_of_day: end_of_day)
93
+ end
94
+
95
+ weekday = parse_weekday_phrase(normalized)
96
+ return nil unless weekday
97
+
98
+ time_for_date(weekday, end_of_day: end_of_day)
99
+ end
100
+
101
+ def parse_weekday_phrase(normalized)
102
+ tokens = normalized.split(/\s+/)
103
+ return nil if tokens.empty?
104
+
105
+ qualifier = nil
106
+ day_token = nil
107
+
108
+ if tokens.length == 1
109
+ day_token = tokens[0]
110
+ elsif tokens.length == 2
111
+ qualifier = tokens[0]
112
+ day_token = tokens[1]
113
+ else
114
+ return nil
115
+ end
116
+
117
+ wday = WEEKDAYS[day_token]
118
+ return nil unless wday
119
+
120
+ today = Date.today
121
+
122
+ if qualifier.nil? || THIS_WORDS.include?(qualifier)
123
+ next_weekday(today, wday, inclusive: true)
124
+ elsif NEXT_WORDS.include?(qualifier)
125
+ next_weekday(today, wday, inclusive: false)
126
+ elsif LAST_WORDS.include?(qualifier)
127
+ previous_weekday(today, wday, inclusive: false)
128
+ end
129
+ end
130
+
131
+ def next_weekday(from, wday, inclusive:)
132
+ delta = (wday - from.wday) % 7
133
+ delta = 7 if delta.zero? && !inclusive
134
+ from + delta
135
+ end
136
+
137
+ def previous_weekday(from, wday, inclusive:)
138
+ delta = (from.wday - wday) % 7
139
+ delta = 7 if delta.zero? && !inclusive
140
+ from - delta
141
+ end
142
+
143
+ def time_for_date(date, end_of_day:)
144
+ return end_of_day_for(date) if end_of_day
145
+ return [date.to_time, Time.now].max if date == Date.today
146
+
147
+ date.to_time
148
+ end
149
+
59
150
  def formats_for(locale)
60
151
  iso = [
61
152
  [/\A(\d{4})[-\/](\d{1,2})[-\/](\d{1,2})#{TIME_SUFFIX}\z/i, %i[year month day hour min sec]],
@@ -98,10 +189,7 @@ module FlycalCli
98
189
  date = Date.new(year, month, day)
99
190
 
100
191
  if hour.nil?
101
- return end_of_day_for(date) if end_of_day
102
- return [date.to_time, Time.now].max if date == Date.today
103
-
104
- return date.to_time
192
+ return time_for_date(date, end_of_day: end_of_day)
105
193
  end
106
194
 
107
195
  Time.local(year, month, day, hour, min || 0, sec || 0)
@@ -119,9 +207,9 @@ module FlycalCli
119
207
 
120
208
  def invalid_message(str)
121
209
  if Locale.current_locale.to_s == "it"
122
- "Data non valida: #{str.inspect}. Usa YYYY-MM-DD, DD-MM-YYYY (anche con /) o con orario."
210
+ "Data non valida: #{str.inspect}. Usa YYYY-MM-DD, DD-MM-YYYY, oggi/domani, lunedi, prossimo lunedi, ecc."
123
211
  else
124
- "Invalid date: #{str.inspect}. Use YYYY-MM-DD, MM-DD-YYYY (also with /), or with time."
212
+ "Invalid date: #{str.inspect}. Use YYYY-MM-DD, MM-DD-YYYY, today/tomorrow, monday, next monday, etc."
125
213
  end
126
214
  end
127
215
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlycalCli
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.2'
5
5
  end
data/mcp/tools.json CHANGED
@@ -287,7 +287,7 @@
287
287
  },
288
288
  "from": {
289
289
  "type": "string",
290
- "description": "Start date/time. Default: now. YYYY-MM-DD always; with locale it also DD-MM-YYYY; with locale en also MM-DD-YYYY. Separators - or /. Optional time."
290
+ "description": "Start date/time. Default: now. Absolute dates, or relative: monday, next monday, tomorrow, lunedi, prossimo lunedi, etc."
291
291
  },
292
292
  "in": {
293
293
  "type": "string",
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.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - flycal-cli