@bloomai/cli 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -46,12 +46,25 @@ jq '.data[] | select(.revenue_growth > 0.1)' /tmp/bloom/aapl-revenue.json
46
46
  | `screen` | Filter stocks by metrics | `bloom screen "market_cap > 100B" "pe_ratio < 25"` |
47
47
  | `earnings` | Earnings history & estimates | `bloom earnings AAPL` |
48
48
  | `technicals` | RSI, MAs, patterns, options flow | `bloom technicals AAPL` |
49
+ | `ma` | Raw moving average data (SMA/EMA) | `bloom ma AAPL --sma 50,100,200 --ema 20` |
49
50
  | `options-history` | Options sentiment over time | `bloom options-history AAPL --days 30` |
50
51
  | `portfolio` | Portfolio risk analysis | `bloom portfolio '{"AAPL":0.5,"MSFT":0.5}'` |
51
52
  | `trades` | AI trade evaluation | `bloom trades "Buy AAPL: strong AI catalysts"` |
52
53
  | `catalysts` | Forward-looking value drivers | `bloom catalysts AAPL MSFT` |
53
54
  | `position-size` | Kelly criterion sizing | `bloom position-size --bull 0.6 --bear -0.15 --conviction 8 --value 100000` |
54
55
  | `transcript` | Search earnings call transcripts | `bloom transcript AAPL "AI" "services"` |
56
+ | `ai-trades` | Recent AI model trade decisions (InvestingArena) | `bloom ai-trades --model claude --limit 5` |
57
+ | `ai-portfolio` | AI model portfolio positions & performance | `bloom ai-portfolio --model openai` |
58
+ | `search` | Web search via Serper (Google) | `bloom search "nvidia earnings Q1 2026"` |
59
+ | `scrape` | Scrape a web page via Firecrawl | `bloom scrape https://example.com` |
60
+
61
+ ## Environment Variables
62
+
63
+ | Variable | Required for | Description |
64
+ |----------|-------------|-------------|
65
+ | `BLOOM_API_KEY` | All financial commands | Bloom API key (`bloom auth login`) |
66
+ | `SERPER_API_KEY` | `bloom search` | Serper.dev API key for web search |
67
+ | `FIRECRAWL_API_KEY` | `bloom scrape` | Firecrawl API key for web scraping |
55
68
 
56
69
  ## Global Flags
57
70
 
@@ -115,6 +128,57 @@ bloom transcript AAPL "AI" "services" "margins" --max 20 -o /tmp/bloom/transcrip
115
128
  jq '.matches[] | {speaker, text}' /tmp/bloom/transcript.json
116
129
  ```
117
130
 
131
+ ### Moving Averages
132
+ ```bash
133
+ # Default: 100-day SMA + 100-day EMA
134
+ bloom ma AAPL
135
+
136
+ # Custom periods
137
+ bloom ma AAPL --sma 50,100,200 --ema 20,100
138
+
139
+ # Last 50 data points, save to file
140
+ bloom ma AAPL --sma 200 --limit 50 -o /tmp/bloom/aapl-ma.json
141
+
142
+ # Extract latest values
143
+ jq '.indicators | to_entries[] | {(.key): .value | to_entries | last | .value}' /tmp/bloom/aapl-ma.json
144
+ ```
145
+
146
+ ### AI Arena (InvestingArena — 3 AIs with real money)
147
+ ```bash
148
+ # All AI models' recent trades
149
+ bloom ai-trades -o /tmp/bloom/ai-trades.json
150
+
151
+ # Claude's last 5 trades
152
+ bloom ai-trades --model claude --limit 5
153
+
154
+ # All trades on NVDA across all models
155
+ bloom ai-trades --symbol NVDA -o /tmp/bloom/nvda-ai-trades.json
156
+
157
+ # Full portfolio snapshot for all 3 models
158
+ bloom ai-portfolio -o /tmp/bloom/ai-portfolio.json
159
+
160
+ # Just Gemini's portfolio
161
+ bloom ai-portfolio --model gemini
162
+
163
+ # Extract model returns
164
+ jq '[.[] | {model: .ai_model, return_pct, account_value}]' /tmp/bloom/ai-portfolio.json
165
+ ```
166
+
167
+ ### Web Research (Search + Scrape)
168
+ ```bash
169
+ # Search the web
170
+ bloom search "nvidia earnings Q1 2026" --limit 5 -o /tmp/bloom/search.json
171
+
172
+ # Search for news specifically
173
+ bloom search "AAPL product launch" --type news --limit 10
174
+
175
+ # Scrape a page for full content
176
+ bloom scrape https://example.com/article -o /tmp/bloom/article.md
177
+
178
+ # Truncate long pages
179
+ bloom scrape https://example.com/article --max-chars 5000
180
+ ```
181
+
118
182
  ## Tips
119
183
 
120
184
  - Use `-o` to download data to files, then `jq` to extract. Saves context tokens.
package/SKILL.md CHANGED
@@ -48,6 +48,18 @@ jq '.data[] | select(.revenue_growth > 0.1)' /tmp/bloom/aapl-revenue.json
48
48
  | `catalysts` | Forward-looking value drivers | `bloom catalysts AAPL MSFT` |
49
49
  | `position-size` | Kelly criterion sizing | `bloom position-size --bull 0.6 --bear -0.15 --conviction 8 --value 100000` |
50
50
  | `transcript` | Search earnings call transcripts | `bloom transcript AAPL "AI" "services"` |
51
+ | `ai-trades` | Recent AI model trade decisions (InvestingArena) | `bloom ai-trades --model claude --limit 5` |
52
+ | `ai-portfolio` | AI model portfolio positions & performance | `bloom ai-portfolio --model openai` |
53
+ | `search` | Web search via Serper (Google) | `bloom search "nvidia earnings Q1 2026"` |
54
+ | `scrape` | Scrape a web page via Firecrawl | `bloom scrape https://example.com` |
55
+
56
+ ## Environment Variables
57
+
58
+ | Variable | Required for | Description |
59
+ |----------|-------------|-------------|
60
+ | `BLOOM_API_KEY` | All financial commands | Bloom API key (`bloom auth login`) |
61
+ | `SERPER_API_KEY` | `bloom search` | Serper.dev API key for web search |
62
+ | `FIRECRAWL_API_KEY` | `bloom scrape` | Firecrawl API key for web scraping |
51
63
 
52
64
  ## Global Flags
53
65
 
@@ -111,6 +123,27 @@ bloom transcript AAPL "AI" "services" "margins" --max 20 -o /tmp/bloom/transcrip
111
123
  jq '.matches[] | {speaker, text}' /tmp/bloom/transcript.json
112
124
  ```
113
125
 
126
+ ### AI Arena (InvestingArena — 3 AIs with real money)
127
+ ```bash
128
+ # All AI models' recent trades
129
+ bloom ai-trades -o /tmp/bloom/ai-trades.json
130
+
131
+ # Claude's last 5 trades
132
+ bloom ai-trades --model claude --limit 5
133
+
134
+ # All trades on NVDA across all models
135
+ bloom ai-trades --symbol NVDA -o /tmp/bloom/nvda-ai-trades.json
136
+
137
+ # Full portfolio snapshot for all 3 models
138
+ bloom ai-portfolio -o /tmp/bloom/ai-portfolio.json
139
+
140
+ # Just Gemini's portfolio
141
+ bloom ai-portfolio --model gemini
142
+
143
+ # Extract model returns
144
+ jq '[.[] | {model: .ai_model, return_pct, account_value}]' /tmp/bloom/ai-portfolio.json
145
+ ```
146
+
114
147
  ## Tips
115
148
 
116
149
  - Use `-o` to download data to files, then `jq` to extract. Saves context tokens.
package/dist/index.js CHANGED
@@ -452,7 +452,9 @@ var CHART_TYPES = [
452
452
  "net_earnings",
453
453
  "net_earnings_growth",
454
454
  "free_cash_flow",
455
- "free_cash_flow_growth"
455
+ "free_cash_flow_growth",
456
+ "stock_based_compensation",
457
+ "stock_based_compensation_growth"
456
458
  ];
457
459
  function registerFinancialsCommand(program2) {
458
460
  addGlobalOptions(
@@ -511,12 +513,13 @@ function formatSentiment(data) {
511
513
  // src/commands/market.ts
512
514
  function registerMarketCommand(program2) {
513
515
  addGlobalOptions(
514
- program2.command("market").description("Market performance, sectors, global").option("--type <type>", "top_movers|sector_breakdown|global_markets|major_indexes", "top_movers").option("--period <p>", "1d|1w|1m|3m|6m|ytd|1y|5y", "1d").option("--limit <n>", "Max stocks for top_movers", "24")
516
+ program2.command("market").description("Market performance, sectors, global").option("--type <type>", "top_movers|sector_breakdown|global_markets|major_indexes", "top_movers").option("--period <p>", "1d|1w|1m|3m|6m|ytd|1y|5y", "1d").option("--limit <n>", "Max stocks for top_movers", "24").option("--universe <u>", "all|sp500|international|crypto|community", "all")
515
517
  ).action(async (opts) => {
516
518
  const data = await callTool("get_market_map", {
517
519
  map_type: opts.type,
518
520
  period: opts.period,
519
- limit: parseIntSafe(opts.limit, 24)
521
+ limit: parseIntSafe(opts.limit, 24),
522
+ universe: opts.universe
520
523
  });
521
524
  outputResult(data, opts);
522
525
  });
@@ -782,12 +785,350 @@ function formatTranscript(data, opts) {
782
785
  return makeTable(["Speaker", "Role", "Text"], rows, opts);
783
786
  }
784
787
 
788
+ // src/commands/ai-trades.ts
789
+ function registerAiTradesCommand(program2) {
790
+ addGlobalOptions(
791
+ program2.command("ai-trades").description("Recent trade decisions from AI models competing with real money (InvestingArena)").option("--model <m>", "Filter by AI model: claude|openai|gemini").option("--symbol <s>", "Filter by stock ticker symbol (e.g., AAPL)").option("--limit <n>", "Max recent trades per model (default: 10, max: 50)", "10")
792
+ ).action(async (opts) => {
793
+ const args = {
794
+ limit: parseIntSafe(opts.limit, 10)
795
+ };
796
+ if (opts.model) args.ai_model = opts.model;
797
+ if (opts.symbol) args.symbol = opts.symbol.toUpperCase();
798
+ const data = await callTool("get_ai_trade_ideas", args);
799
+ if (opts.raw) {
800
+ outputResult(data, opts);
801
+ return;
802
+ }
803
+ const obj = data;
804
+ const trades = obj.recent_trades ?? data;
805
+ outputResult(trades, opts, (d, o) => formatTrades(d, o));
806
+ });
807
+ }
808
+ function formatTrades(data, opts) {
809
+ if (!data || !Array.isArray(data)) {
810
+ if (data && typeof data === "object") {
811
+ return JSON.stringify(data, null, 2);
812
+ }
813
+ return String(data ?? "(no trades)");
814
+ }
815
+ if (data.length === 0) return "(no trades found)";
816
+ const rows = data.map((trade) => {
817
+ const t = trade;
818
+ const gain = t.realized_gain_loss != null ? `$${Number(t.realized_gain_loss).toFixed(2)}` : "-";
819
+ return [
820
+ String(t.ai_model ?? t.model ?? "-"),
821
+ String(t.action ?? "-").toUpperCase(),
822
+ String(t.symbol ?? "-"),
823
+ String(t.shares ?? "-"),
824
+ t.fill_price != null ? `$${Number(t.fill_price).toFixed(2)}` : "-",
825
+ gain,
826
+ truncate2(String(t.rationale ?? "-"), 60)
827
+ ];
828
+ });
829
+ return makeTable(
830
+ ["Model", "Action", "Symbol", "Shares", "Fill Price", "Realized P/L", "Rationale"],
831
+ rows,
832
+ opts
833
+ );
834
+ }
835
+ function truncate2(s, max) {
836
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
837
+ }
838
+
839
+ // src/commands/ai-portfolio.ts
840
+ function registerAiPortfolioCommand(program2) {
841
+ addGlobalOptions(
842
+ program2.command("ai-portfolio").description("Current portfolio positions held by AI models (InvestingArena live accounts)").option("--model <m>", "Filter by AI model: claude|openai|gemini")
843
+ ).action(async (opts) => {
844
+ const args = {};
845
+ if (opts.model) args.ai_model = opts.model;
846
+ const data = await callTool("get_ai_trade_ideas", args);
847
+ if (opts.raw) {
848
+ outputResult(data, opts);
849
+ return;
850
+ }
851
+ const obj = data;
852
+ const portfolios = obj.portfolios ?? data;
853
+ outputResult(portfolios, opts, (d, o) => formatPortfolios(d, o));
854
+ });
855
+ }
856
+ function formatPortfolios(data, opts) {
857
+ if (!data || !Array.isArray(data)) {
858
+ if (data && typeof data === "object") {
859
+ return JSON.stringify(data, null, 2);
860
+ }
861
+ return String(data ?? "(no portfolio data)");
862
+ }
863
+ if (data.length === 0) return "(no portfolio data found)";
864
+ const lines = [];
865
+ for (const portfolio of data) {
866
+ const p = portfolio;
867
+ const modelName = String(p.ai_model ?? p.model ?? "Unknown").toUpperCase();
868
+ lines.push(`
869
+ === ${modelName} ===`);
870
+ const acctValue = p.account_value != null ? `$${Number(p.account_value).toFixed(2)}` : "-";
871
+ const cash = p.cash != null ? `$${Number(p.cash).toFixed(2)}` : "-";
872
+ const returnPct = p.return_pct != null ? `${Number(p.return_pct).toFixed(2)}%` : "-";
873
+ const pnl = p.total_pnl != null ? `$${Number(p.total_pnl).toFixed(2)}` : "-";
874
+ lines.push(` Account Value: ${acctValue} | Cash: ${cash} | Return: ${returnPct} | Total P&L: ${pnl}`);
875
+ const positions = p.positions;
876
+ if (Array.isArray(positions) && positions.length > 0) {
877
+ lines.push("");
878
+ const rows = positions.map((pos) => {
879
+ const position = pos;
880
+ const unrealizedPnl = position.unrealized_pnl != null ? `$${Number(position.unrealized_pnl).toFixed(2)}` : "-";
881
+ const allocation = position.allocation_pct != null ? `${Number(position.allocation_pct).toFixed(1)}%` : "-";
882
+ const avgCost = position.avg_cost != null ? `$${Number(position.avg_cost).toFixed(2)}` : "-";
883
+ return [
884
+ String(position.symbol ?? "-"),
885
+ String(position.qty ?? "-"),
886
+ avgCost,
887
+ unrealizedPnl,
888
+ allocation
889
+ ];
890
+ });
891
+ lines.push(
892
+ makeTable(["Symbol", "Qty", "Avg Cost", "Unrealized P/L", "Allocation"], rows, opts)
893
+ );
894
+ } else {
895
+ lines.push(" (no open positions)");
896
+ }
897
+ }
898
+ return lines.join("\n");
899
+ }
900
+
901
+ // src/commands/ma.ts
902
+ function formatTable(data, limit) {
903
+ const { symbol, current_price, indicators } = data;
904
+ const labels = Object.keys(indicators);
905
+ if (labels.length === 0) {
906
+ console.log(`No indicator data returned for ${symbol}`);
907
+ return;
908
+ }
909
+ const allDates = /* @__PURE__ */ new Set();
910
+ for (const label of labels) {
911
+ const ind = indicators[label];
912
+ if ("error" in ind) continue;
913
+ for (const date of Object.keys(ind)) {
914
+ allDates.add(date);
915
+ }
916
+ }
917
+ const sortedDates = Array.from(allDates).sort((a, b) => b.localeCompare(a)).slice(0, limit).reverse();
918
+ if (sortedDates.length === 0) {
919
+ console.log(`No data points available for ${symbol}`);
920
+ return;
921
+ }
922
+ const dateWidth = 12;
923
+ const colWidth = 12;
924
+ const header = "Date".padEnd(dateWidth) + labels.map((l) => l.toUpperCase().padStart(colWidth)).join("");
925
+ const divider = "-".repeat(dateWidth + colWidth * labels.length);
926
+ console.log(`
927
+ ${symbol}${current_price != null ? ` (current: $${current_price.toFixed(2)})` : ""}`);
928
+ console.log(divider);
929
+ console.log(header);
930
+ console.log(divider);
931
+ for (const date of sortedDates) {
932
+ let row = date.padEnd(dateWidth);
933
+ for (const label of labels) {
934
+ const ind = indicators[label];
935
+ if ("error" in ind) {
936
+ row += "N/A".padStart(colWidth);
937
+ } else {
938
+ const val = ind[date];
939
+ row += (val != null ? val.toFixed(2) : "\u2014").padStart(colWidth);
940
+ }
941
+ }
942
+ console.log(row);
943
+ }
944
+ console.log(divider);
945
+ console.log(`Showing ${sortedDates.length} data point(s)
946
+ `);
947
+ }
948
+ function registerMaCommand(program2) {
949
+ addGlobalOptions(
950
+ program2.command("ma").description("Moving averages (SMA/EMA) for a symbol").argument("<symbol>", "Ticker symbol (e.g., AAPL)").option("--sma <periods>", "Comma-separated SMA periods (e.g., 50,100,200)").option("--ema <periods>", "Comma-separated EMA periods (e.g., 20,50)").option("--limit <n>", "Number of data points to display (default: 20)", "20")
951
+ ).action(
952
+ async (symbol, opts) => {
953
+ const indicators = [];
954
+ if (opts.sma) {
955
+ for (const p of opts.sma.split(",")) {
956
+ const period = p.trim();
957
+ if (period) indicators.push(`sma_${period}`);
958
+ }
959
+ }
960
+ if (opts.ema) {
961
+ for (const p of opts.ema.split(",")) {
962
+ const period = p.trim();
963
+ if (period) indicators.push(`ema_${period}`);
964
+ }
965
+ }
966
+ if (indicators.length === 0) {
967
+ indicators.push("sma_100", "ema_100");
968
+ }
969
+ const args = {
970
+ symbol: symbol.toUpperCase(),
971
+ indicators
972
+ };
973
+ const data = await callTool("get_moving_averages", args);
974
+ const limit = parseInt(opts.limit, 10) || 20;
975
+ let parsed = null;
976
+ try {
977
+ parsed = typeof data === "string" ? JSON.parse(data) : data;
978
+ } catch {
979
+ }
980
+ if (parsed?.indicators && typeof parsed.indicators === "object") {
981
+ const allDates = /* @__PURE__ */ new Set();
982
+ for (const ind of Object.values(parsed.indicators)) {
983
+ if ("error" in ind) continue;
984
+ for (const date of Object.keys(ind)) {
985
+ allDates.add(date);
986
+ }
987
+ }
988
+ const keepDates = new Set(
989
+ Array.from(allDates).sort((a, b) => b.localeCompare(a)).slice(0, limit)
990
+ );
991
+ for (const [label, ind] of Object.entries(parsed.indicators)) {
992
+ if ("error" in ind) continue;
993
+ const filtered = {};
994
+ for (const [date, val] of Object.entries(ind)) {
995
+ if (keepDates.has(date)) filtered[date] = val;
996
+ }
997
+ parsed.indicators[label] = filtered;
998
+ }
999
+ }
1000
+ if (opts.output || opts.format || opts.raw || !process.stdout.isTTY) {
1001
+ outputResult(parsed ?? data, opts);
1002
+ return;
1003
+ }
1004
+ if (parsed?.indicators && typeof parsed.indicators === "object") {
1005
+ formatTable(parsed, limit);
1006
+ } else {
1007
+ outputResult(data, opts);
1008
+ }
1009
+ }
1010
+ );
1011
+ }
1012
+
1013
+ // src/commands/search.ts
1014
+ function registerSearchCommand(program2) {
1015
+ addGlobalOptions(
1016
+ program2.command("search").description("Web search via Serper (Google)").argument("<query...>", "Search query (joined into one string)").option("--limit <n>", "Max results (default: 10)", "10").option("--type <type>", "Search type: web, news, images (default: web)", "web")
1017
+ ).action(async (query, opts) => {
1018
+ const apiKey = process.env.SERPER_API_KEY;
1019
+ if (!apiKey) {
1020
+ console.error("Error: SERPER_API_KEY environment variable is required.");
1021
+ process.exit(1);
1022
+ }
1023
+ const q = query.join(" ");
1024
+ const limit = parseIntSafe(opts.limit, 10);
1025
+ const searchType = opts.type || "web";
1026
+ const validTypes = /* @__PURE__ */ new Set(["web", "news", "images"]);
1027
+ if (!validTypes.has(searchType)) {
1028
+ console.error(`Error: Invalid search type "${searchType}". Must be one of: web, news, images`);
1029
+ process.exit(1);
1030
+ }
1031
+ const endpoint = searchType === "web" ? "search" : searchType;
1032
+ const response = await fetch(`https://google.serper.dev/${endpoint}`, {
1033
+ method: "POST",
1034
+ headers: {
1035
+ "X-API-KEY": apiKey,
1036
+ "Content-Type": "application/json"
1037
+ },
1038
+ body: JSON.stringify({ q, num: limit })
1039
+ });
1040
+ if (!response.ok) {
1041
+ const text = await response.text();
1042
+ console.error(`Serper API error (${response.status}): ${text}`);
1043
+ process.exit(1);
1044
+ }
1045
+ const data = await response.json();
1046
+ outputResult(data, opts, (d, o) => formatSearch(d, o));
1047
+ });
1048
+ }
1049
+ function formatSearch(data, opts) {
1050
+ if (!data || typeof data !== "object") return String(data);
1051
+ const obj = data;
1052
+ const organic = obj.organic || obj.news || obj.images || [];
1053
+ if (!Array.isArray(organic) || organic.length === 0) return "(no results)";
1054
+ const rows = organic.map((r) => [
1055
+ String(r.position ?? ""),
1056
+ String(r.title || "").slice(0, 60),
1057
+ String(r.link || "").slice(0, 60),
1058
+ String(r.snippet || r.description || "").slice(0, 80)
1059
+ ]);
1060
+ return makeTable(["#", "Title", "URL", "Snippet"], rows, opts);
1061
+ }
1062
+
1063
+ // src/commands/scrape.ts
1064
+ function registerScrapeCommand(program2) {
1065
+ addGlobalOptions(
1066
+ program2.command("scrape").description("Scrape a web page via Firecrawl").argument("<url>", "URL to scrape").option("--max-chars <n>", "Truncate output to N characters")
1067
+ ).action(async (url, opts) => {
1068
+ if (opts.maxChars !== void 0 && isNaN(parseInt(opts.maxChars, 10))) {
1069
+ console.error("Error: --max-chars must be a number");
1070
+ process.exit(1);
1071
+ }
1072
+ const apiKey = process.env.FIRECRAWL_API_KEY;
1073
+ if (!apiKey) {
1074
+ console.error("Error: FIRECRAWL_API_KEY environment variable is required.");
1075
+ process.exit(1);
1076
+ }
1077
+ const response = await fetch("https://api.firecrawl.dev/v1/scrape", {
1078
+ method: "POST",
1079
+ headers: {
1080
+ Authorization: `Bearer ${apiKey}`,
1081
+ "Content-Type": "application/json"
1082
+ },
1083
+ body: JSON.stringify({ url, formats: ["markdown"] })
1084
+ });
1085
+ if (!response.ok) {
1086
+ const text = await response.text();
1087
+ console.error(`Firecrawl API error (${response.status}): ${text}`);
1088
+ process.exit(1);
1089
+ }
1090
+ const data = await response.json();
1091
+ outputResult(data, opts, (d, o) => formatScrape(d, o, opts.maxChars));
1092
+ });
1093
+ }
1094
+ function formatScrape(data, _opts, maxChars) {
1095
+ if (!data || typeof data !== "object") return String(data);
1096
+ const obj = data;
1097
+ const inner = obj.data || obj;
1098
+ const content = String(inner.markdown || inner.text || inner.content || "(no content)");
1099
+ const title = inner.metadata ? String(inner.metadata.title || "") : "";
1100
+ const header = title ? `# ${title}
1101
+
1102
+ ` : "";
1103
+ const body = header + content;
1104
+ if (maxChars) {
1105
+ const limit = parseIntSafe(maxChars, body.length);
1106
+ if (body.length > limit) {
1107
+ return body.slice(0, limit) + "\n\n... (truncated)";
1108
+ }
1109
+ }
1110
+ return body;
1111
+ }
1112
+
785
1113
  // src/index.ts
786
1114
  var program = new Command();
787
1115
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
788
1116
  var pkgPath = path.join(__dirname, "..", "package.json");
789
- var pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
790
- program.name("bloom").description("Bloom CLI \u2014 financial data for coding agents and humans").version(pkg.version);
1117
+ var compiledVersion = "0.1.4".length > 0 ? "0.1.4" : void 0;
1118
+ var envVersion = process.env.BLOOM_CLI_VERSION ?? process.env.npm_package_version;
1119
+ var fallbackVersion = compiledVersion ?? "unknown";
1120
+ var cliVersion = envVersion ?? fallbackVersion;
1121
+ try {
1122
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
1123
+ if (typeof pkg.version === "string") {
1124
+ cliVersion = pkg.version;
1125
+ }
1126
+ } catch {
1127
+ }
1128
+ if (cliVersion === "0.0.0") {
1129
+ cliVersion = fallbackVersion;
1130
+ }
1131
+ program.name("bloom").description("Bloom CLI \u2014 financial data for coding agents and humans").version(cliVersion);
791
1132
  registerAuthCommand(program);
792
1133
  registerInfoCommand(program);
793
1134
  registerNewsCommand(program);
@@ -805,6 +1146,11 @@ registerTradesCommand(program);
805
1146
  registerCatalystsCommand(program);
806
1147
  registerPositionCommand(program);
807
1148
  registerTranscriptCommand(program);
1149
+ registerAiTradesCommand(program);
1150
+ registerAiPortfolioCommand(program);
1151
+ registerMaCommand(program);
1152
+ registerSearchCommand(program);
1153
+ registerScrapeCommand(program);
808
1154
  program.parseAsync().catch((err) => {
809
1155
  console.error("Error:", err.message || err);
810
1156
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloomai/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI tool for Bloom financial data - download market data to files for analysis",
5
5
  "type": "module",
6
6
  "bin": {