agent-finance-cli 0.1.1 → 0.1.3

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.
package/src/skills.rs CHANGED
@@ -18,7 +18,11 @@ const SKILLS: &[BuiltinSkill] = &[
18
18
  },
19
19
  BuiltinSkill {
20
20
  name: "providers",
21
- description: "Understand Yahoo, SEC EDGAR, CNBC, Robinhood, Stooq, Binance futures, and proxy quote capabilities.",
21
+ description: "Understand Yahoo, SEC EDGAR, CNBC, Robinhood, Stooq, Binance futures, Polymarket, and proxy quote capabilities.",
22
+ },
23
+ BuiltinSkill {
24
+ name: "prediction-markets",
25
+ description: "Use Polymarket as quantifiable sentiment and event-probability evidence via official read-only APIs.",
22
26
  },
23
27
  BuiltinSkill {
24
28
  name: "history-indicators",
@@ -43,6 +47,7 @@ pub fn get(name: &str, full: bool) -> Option<&'static str> {
43
47
  ("price", _) => Some(PRICE),
44
48
  ("research-data", _) => Some(RESEARCH_DATA),
45
49
  ("providers", _) => Some(PROVIDERS),
50
+ ("prediction-markets", _) => Some(PREDICTION_MARKETS),
46
51
  ("history-indicators", _) => Some(HISTORY_INDICATORS),
47
52
  ("futures", _) => Some(FUTURES),
48
53
  _ => None,
@@ -54,5 +59,6 @@ const CORE_FULL: &str = include_str!("../skills/core-full.md");
54
59
  const PRICE: &str = include_str!("../skills/price.md");
55
60
  const RESEARCH_DATA: &str = include_str!("../skills/research-data.md");
56
61
  const PROVIDERS: &str = include_str!("../skills/providers.md");
62
+ const PREDICTION_MARKETS: &str = include_str!("../skills/prediction-markets.md");
57
63
  const HISTORY_INDICATORS: &str = include_str!("../skills/history-indicators.md");
58
64
  const FUTURES: &str = include_str!("../skills/futures.md");
package/src/time.rs CHANGED
@@ -1,7 +1,15 @@
1
+ use anyhow::{Result, anyhow};
1
2
  use chrono::{DateTime, SecondsFormat, Utc};
2
3
  use chrono_tz::Tz;
3
4
 
4
- pub const DEFAULT_TIMEZONE: &str = "Asia/Singapore";
5
+ const FALLBACK_TIMEZONE: &str = "UTC";
6
+
7
+ pub fn resolve_timezone(value: Option<&str>) -> Result<String> {
8
+ match value {
9
+ Some(value) => parse_timezone_name(value).map(str::to_string),
10
+ None => Ok(system_timezone()),
11
+ }
12
+ }
5
13
 
6
14
  pub fn utc_to_local(value: Option<&str>, timezone: &str) -> Option<String> {
7
15
  let value = value?;
@@ -14,8 +22,68 @@ pub fn now_local(timezone: &str) -> String {
14
22
  }
15
23
 
16
24
  pub fn format_local(datetime: DateTime<Utc>, timezone: &str) -> String {
17
- let timezone = timezone.parse::<Tz>().unwrap_or(chrono_tz::Asia::Singapore);
25
+ let timezone = parse_timezone(timezone).expect("timezone must be resolved before formatting");
18
26
  datetime
19
27
  .with_timezone(&timezone)
20
28
  .to_rfc3339_opts(SecondsFormat::Secs, true)
21
29
  }
30
+
31
+ fn system_timezone() -> String {
32
+ iana_time_zone::get_timezone()
33
+ .ok()
34
+ .and_then(|value| parse_timezone_name(&value).ok().map(str::to_string))
35
+ .unwrap_or_else(|| FALLBACK_TIMEZONE.to_string())
36
+ }
37
+
38
+ fn parse_timezone_name(value: &str) -> Result<&str> {
39
+ let value = value.trim();
40
+ if parse_timezone(value).is_ok() {
41
+ Ok(value)
42
+ } else {
43
+ Err(anyhow!("invalid IANA timezone: {value}"))
44
+ }
45
+ }
46
+
47
+ fn parse_timezone(value: &str) -> Result<Tz> {
48
+ value
49
+ .parse::<Tz>()
50
+ .map_err(|_| anyhow!("invalid IANA timezone: {value}"))
51
+ }
52
+
53
+ #[cfg(test)]
54
+ mod tests {
55
+ use super::*;
56
+
57
+ #[test]
58
+ fn explicit_timezone_overrides_system_timezone() {
59
+ assert_eq!(resolve_timezone(Some("UTC")).unwrap(), "UTC");
60
+ }
61
+
62
+ #[test]
63
+ fn invalid_explicit_timezone_is_rejected() {
64
+ assert!(resolve_timezone(Some("Not/AZone")).is_err());
65
+ }
66
+
67
+ #[test]
68
+ fn default_timezone_uses_system_timezone_when_available() {
69
+ let expected = iana_time_zone::get_timezone()
70
+ .ok()
71
+ .and_then(|value| parse_timezone_name(&value).ok().map(str::to_string))
72
+ .unwrap_or_else(|| FALLBACK_TIMEZONE.to_string());
73
+
74
+ assert_eq!(resolve_timezone(None).unwrap(), expected);
75
+ }
76
+
77
+ #[test]
78
+ fn local_format_uses_explicit_timezone() {
79
+ let datetime = DateTime::parse_from_rfc3339("2026-06-21T00:00:00Z")
80
+ .unwrap()
81
+ .with_timezone(&Utc);
82
+
83
+ assert_eq!(format_local(datetime, "UTC"), "2026-06-21T00:00:00Z");
84
+ assert_eq!(
85
+ format_local(datetime, "America/New_York"),
86
+ "2026-06-20T20:00:00-04:00"
87
+ );
88
+ }
89
+ }