@insightsentry/mcp 1.4.14 → 1.4.16

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/dist/cli.d.ts CHANGED
@@ -11,15 +11,26 @@ export declare function buildHelp(): string;
11
11
  export declare function buildDownloadHistoryHelp(): string;
12
12
  export declare function buildToolHelp(tool: ToolDefinition): string;
13
13
  type RequestFn = (method: string, pathTemplate: string, params: Record<string, any>) => Promise<any>;
14
+ type RunCommandFn = (command: string, args: string[]) => Promise<{
15
+ stdout?: string;
16
+ stderr?: string;
17
+ }>;
14
18
  interface CliIO {
15
19
  write: (s: string) => void;
16
20
  exit: (code: number) => void;
17
21
  request?: RequestFn;
22
+ createRequestFromApiKey?: (apiKey: string) => RequestFn;
23
+ runCommand?: RunCommandFn;
18
24
  progress?: (s: string) => void;
19
25
  prompt?: (question: string) => Promise<string>;
26
+ selectTool?: (options: ToolSelectionOption[]) => Promise<string | null>;
20
27
  isInteractive?: boolean;
21
28
  getAuthStatus?: () => AuthStatus;
22
29
  }
30
+ interface ToolSelectionOption {
31
+ name: string;
32
+ description: string;
33
+ }
23
34
  export declare function runCli(argv: string[], io: CliIO): Promise<void>;
24
35
  export declare function main(): void;
25
36
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAyC,MAAM,kBAAkB,CAAC;AAgB1F,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,uBAAuB,CAAC;AAG7E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAwB/C,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoCpD;AAED,wBAAgB,SAAS,IAAI,MAAM,CA6ClC;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CA8BjD;AAyFD,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAsC1D;AAkCD,KAAK,SAAS,GAAG,CACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KACxB,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB,UAAU,KAAK;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,UAAU,CAAC;CAClC;AAKD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAiMrE;AAymBD,wBAAgB,IAAI,SASnB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,UAAU,EAAyC,MAAM,kBAAkB,CAAC;AAgB1F,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,uBAAuB,CAAC;AAG7E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA8B/C,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoCpD;AAED,wBAAgB,SAAS,IAAI,MAAM,CA8ClC;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CA8BjD;AAyFD,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAsC1D;AAkCD,KAAK,SAAS,GAAG,CACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KACxB,OAAO,CAAC,GAAG,CAAC,CAAC;AAClB,KAAK,YAAY,GAAG,CAClB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEnD,UAAU,KAAK;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,uBAAuB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;IACxD,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,UAAU,CAAC;CAClC;AAKD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA+OrE;AA04BD,wBAAgB,IAAI,SAWnB"}
package/dist/cli.js CHANGED
@@ -1,5 +1,8 @@
1
+ import { execFile } from "node:child_process";
1
2
  import { stdin as input, stdout as output } from "node:process";
3
+ import { emitKeypressEvents } from "node:readline";
2
4
  import { createInterface } from "node:readline/promises";
5
+ import { promisify } from "node:util";
3
6
  import { z } from "zod";
4
7
  import { ApiClient } from "./api-client.js";
5
8
  import { coerceArgs, getZodEnumValues, getZodTypeName, isOptionalZodType } from "./arg-coercion.js";
@@ -12,10 +15,12 @@ import { supportsCsvStorage, validateResponseStorageTarget, } from "./response-s
12
15
  import { analyzeSymbolCodes, shouldPromptSymbolScopedParam } from "./symbol-param-applicability.js";
13
16
  import { validateSymbolLikeArg } from "./symbol-validation.js";
14
17
  import { toolDefinitions } from "./tool-definitions.js";
15
- import { runApiTool } from "./tool-runner.js";
18
+ import { runApiTool, validateFilterExpression } from "./tool-runner.js";
16
19
  export { coerceArgs } from "./arg-coercion.js";
17
20
  const DOWNLOAD_HISTORY_COMMAND = "download_history";
21
+ const UPDATE_COMMAND = "update";
18
22
  const MAX_INTERACTIVE_PROMPT_ATTEMPTS = 3;
23
+ const execFileAsync = promisify(execFile);
19
24
  const STORAGE_DESTINATION_SCHEMA = {
20
25
  output_file: z.string().describe("File path for stored response.").optional(),
21
26
  output_dir: z
@@ -33,6 +38,10 @@ const CSV_STORE_SCHEMA = z
33
38
  .default("none")
34
39
  .optional()
35
40
  .describe("Store original API response before filtering. CSV is available for series/history responses. Default: none.");
41
+ const FILTER_SCHEMA = z
42
+ .string()
43
+ .optional()
44
+ .describe("JSONata expression to transform the response. Leave empty for no filter.");
36
45
  export function parseArgs(argv) {
37
46
  const args = {};
38
47
  let toolName = null;
@@ -91,13 +100,14 @@ export function buildHelp() {
91
100
  lines.push(" insight get_earnings --c US");
92
101
  lines.push(' insight list_options --code "NASDAQ:AAPL" --type call --range 10');
93
102
  lines.push("");
94
- lines.push("All tools support --filter <jsonata> to transform the response.");
103
+ lines.push("API tools support --filter <jsonata> to transform the response.");
95
104
  lines.push("Use: insight <tool> --help for tool-specific parameters.");
96
105
  lines.push("");
97
106
  lines.push("Authentication:");
98
107
  lines.push(" insight login --key <your-api-key> Save API key (persisted across sessions)");
99
108
  lines.push(" insight whoami Print the logged-in user's email");
100
109
  lines.push(" insight logout Remove saved API key");
110
+ lines.push(" insight update Update this CLI with npm");
101
111
  lines.push("");
102
112
  lines.push(" Or set INSIGHTSENTRY_API_KEY environment variable (takes priority over saved key).");
103
113
  lines.push(" Get your API key from https://insightsentry.com/dashboard");
@@ -281,7 +291,24 @@ function formatTypeName(t) {
281
291
  }
282
292
  const REQUIRED_DOWNLOAD_HISTORY_ARGS = ["symbol", "bar_type", "from", "to", "output_dir"];
283
293
  export async function runCli(argv, io) {
284
- const { toolName, args, help } = parseArgs(argv);
294
+ let { toolName, args, help } = parseArgs(argv);
295
+ let sessionApiKey;
296
+ if (!toolName) {
297
+ if (!help && io.isInteractive === true && io.prompt) {
298
+ const selected = await resolveInteractiveToolName(io);
299
+ if (!selected) {
300
+ io.exit(1);
301
+ return;
302
+ }
303
+ toolName = selected.toolName;
304
+ sessionApiKey = selected.apiKey;
305
+ }
306
+ else {
307
+ io.write(buildHelp());
308
+ io.exit(0);
309
+ return;
310
+ }
311
+ }
285
312
  if (!toolName) {
286
313
  io.write(buildHelp());
287
314
  io.exit(0);
@@ -312,6 +339,10 @@ export async function runCli(argv, io) {
312
339
  io.exit(0);
313
340
  return;
314
341
  }
342
+ if (toolName === UPDATE_COMMAND) {
343
+ await runUpdateCommand(io);
344
+ return;
345
+ }
315
346
  if (toolName === "whoami") {
316
347
  const status = (io.getAuthStatus ?? getAuthStatus)();
317
348
  if (status.subject) {
@@ -330,22 +361,26 @@ export async function runCli(argv, io) {
330
361
  io.exit(0);
331
362
  return;
332
363
  }
333
- const request = resolveRequest(io);
334
- if (!request) {
335
- io.write("Error: No API key found.\n\nSet it with: insight login --key <your-api-key>\nOr export: export INSIGHTSENTRY_API_KEY=your-api-key\n\nGet your API key from https://insightsentry.com/dashboard");
336
- io.exit(1);
337
- return;
338
- }
339
364
  try {
340
- const providedValidationError = validateDownloadHistoryArgs(args);
341
- if (providedValidationError) {
342
- io.write(`Invalid ${downloadHistoryPromptLabel(providedValidationError.key)}: ${providedValidationError.error}\n`);
343
- io.exit(1);
344
- return;
365
+ const inputArgs = { ...args };
366
+ const interactive = io.isInteractive === true && Boolean(io.prompt);
367
+ if (interactive) {
368
+ for (const error of collectInvalidProvidedDownloadHistoryArgs(inputArgs)) {
369
+ io.write(`Invalid ${downloadHistoryPromptLabel(error.key)}: ${error.error}\n`);
370
+ delete inputArgs[error.key];
371
+ }
372
+ }
373
+ else {
374
+ const providedValidationError = validateDownloadHistoryArgs(inputArgs);
375
+ if (providedValidationError) {
376
+ io.write(`Invalid ${downloadHistoryPromptLabel(providedValidationError.key)}: ${providedValidationError.error}\n`);
377
+ io.exit(1);
378
+ return;
379
+ }
345
380
  }
346
- const historyArgs = await resolveDownloadHistoryArgs(args, io);
381
+ const historyArgs = await resolveDownloadHistoryArgs(inputArgs, io);
347
382
  if (!historyArgs) {
348
- io.write(`Error: Missing required options for download_history: ${missingDownloadHistoryArgs(args).join(", ")}\n\nRun: insight download_history --help`);
383
+ io.write(`Error: Missing required options for download_history: ${missingDownloadHistoryArgs(inputArgs).join(", ")}\n\nRun: insight download_history --help`);
349
384
  io.exit(1);
350
385
  return;
351
386
  }
@@ -355,6 +390,12 @@ export async function runCli(argv, io) {
355
390
  io.exit(1);
356
391
  return;
357
392
  }
393
+ const request = resolveRequest(io, sessionApiKey);
394
+ if (!request) {
395
+ io.write("Error: No API key found.\n\nSet it with: insight login --key <your-api-key>\nOr export: export INSIGHTSENTRY_API_KEY=your-api-key\n\nGet your API key from https://insightsentry.com/dashboard");
396
+ io.exit(1);
397
+ return;
398
+ }
358
399
  const result = await downloadHistory(parseDownloadHistoryArgs(historyArgs), {
359
400
  request,
360
401
  onProgress: (event) => {
@@ -384,13 +425,6 @@ export async function runCli(argv, io) {
384
425
  io.exit(0);
385
426
  return;
386
427
  }
387
- // Resolve request function
388
- const request = resolveRequest(io);
389
- if (!request) {
390
- io.write("Error: No API key found.\n\nSet it with: insight login --key <your-api-key>\nOr export: export INSIGHTSENTRY_API_KEY=your-api-key\n\nGet your API key from https://insightsentry.com/dashboard");
391
- io.exit(1);
392
- return;
393
- }
394
428
  const inputArgs = { ...args };
395
429
  const interactive = io.isInteractive === true && Boolean(io.prompt);
396
430
  if (interactive) {
@@ -411,9 +445,27 @@ export async function runCli(argv, io) {
411
445
  }
412
446
  const storeModeError = validateStoreMode(tool.name, inputArgs.store);
413
447
  if (storeModeError) {
414
- io.write(`Invalid Store: ${storeModeError}\n`);
415
- io.exit(1);
416
- return;
448
+ if (interactive) {
449
+ io.write(`Invalid Store: ${storeModeError}\n`);
450
+ delete inputArgs.store;
451
+ }
452
+ else {
453
+ io.write(`Invalid Store: ${storeModeError}\n`);
454
+ io.exit(1);
455
+ return;
456
+ }
457
+ }
458
+ const filterError = validateFilterExpression(inputArgs.filter);
459
+ if (filterError) {
460
+ if (interactive) {
461
+ io.write(`Invalid Filter: ${filterError}\n`);
462
+ delete inputArgs.filter;
463
+ }
464
+ else {
465
+ io.write(`Invalid Filter: ${filterError}\n`);
466
+ io.exit(1);
467
+ return;
468
+ }
417
469
  }
418
470
  const resolvedArgs = await resolveToolArgs(inputArgs, tool, io);
419
471
  if (!resolvedArgs) {
@@ -428,6 +480,13 @@ export async function runCli(argv, io) {
428
480
  io.exit(1);
429
481
  return;
430
482
  }
483
+ // Resolve request function
484
+ const request = resolveRequest(io, sessionApiKey);
485
+ if (!request) {
486
+ io.write("Error: No API key found.\n\nSet it with: insight login --key <your-api-key>\nOr export: export INSIGHTSENTRY_API_KEY=your-api-key\n\nGet your API key from https://insightsentry.com/dashboard");
487
+ io.exit(1);
488
+ return;
489
+ }
431
490
  try {
432
491
  const outputValue = await runApiTool({
433
492
  toolName: tool.name,
@@ -444,6 +503,91 @@ export async function runCli(argv, io) {
444
503
  io.exit(1);
445
504
  }
446
505
  }
506
+ async function resolveInteractiveToolName(io) {
507
+ let apiKey;
508
+ const status = (io.getAuthStatus ?? getAuthStatus)();
509
+ if (!status.authenticated) {
510
+ io.write(status.message);
511
+ apiKey = (await resolveLoginKey({}, io)) ?? undefined;
512
+ if (!apiKey) {
513
+ io.write("Usage: insight login --key <your-api-key>\n\nGet your API key from https://insightsentry.com/dashboard");
514
+ return null;
515
+ }
516
+ saveConfig({ apiKey });
517
+ io.write(`API key saved to ${getConfigLocation()}`);
518
+ }
519
+ const options = interactiveToolOptions();
520
+ if (io.selectTool) {
521
+ const selected = await io.selectTool(options);
522
+ if (!selected)
523
+ io.write("No tool selected.");
524
+ return selected ? { toolName: selected, apiKey } : null;
525
+ }
526
+ io.write(buildInteractiveToolList(options));
527
+ for (let attempt = 0; attempt < MAX_INTERACTIVE_PROMPT_ATTEMPTS; attempt++) {
528
+ const answer = (await io.prompt?.("Choose tool (number or name): "))?.trim() ?? "";
529
+ const selected = parseToolSelection(answer, options);
530
+ if (selected)
531
+ return { toolName: selected, apiKey };
532
+ io.write("Invalid tool selection.");
533
+ }
534
+ io.write("No tool selected.");
535
+ return null;
536
+ }
537
+ async function runUpdateCommand(io) {
538
+ const runCommand = io.runCommand ?? defaultRunCommand;
539
+ io.write("Updating InsightSentry MCP CLI with: npm install -g @insightsentry/mcp");
540
+ try {
541
+ const result = await runCommand("npm", ["install", "-g", "@insightsentry/mcp"]);
542
+ const details = [result.stdout?.trim(), result.stderr?.trim()].filter(Boolean).join("\n");
543
+ if (details)
544
+ io.write(details);
545
+ io.write("InsightSentry MCP CLI updated.");
546
+ io.exit(0);
547
+ }
548
+ catch (error) {
549
+ io.write(`Error: ${error?.message ?? String(error)}`);
550
+ io.exit(1);
551
+ }
552
+ }
553
+ async function defaultRunCommand(command, args) {
554
+ return execFileAsync(command, args);
555
+ }
556
+ function interactiveToolOptions() {
557
+ return [
558
+ ...toolDefinitions.map((tool) => ({
559
+ name: tool.name,
560
+ description: summarizeToolDescription(tool.description),
561
+ })),
562
+ {
563
+ name: DOWNLOAD_HISTORY_COMMAND,
564
+ description: "Download historical ranges to JSON/CSV files",
565
+ },
566
+ ];
567
+ }
568
+ function buildInteractiveToolList(options) {
569
+ return [
570
+ "Choose a tool:",
571
+ "",
572
+ ...options.map((option, index) => `${index + 1}. ${option.name} - ${option.description}`),
573
+ "",
574
+ "Use arrow keys in a TTY, or type a number/name.",
575
+ ].join("\n");
576
+ }
577
+ function summarizeToolDescription(description) {
578
+ return description.split("→")[0].split(".")[0].trim();
579
+ }
580
+ function parseToolSelection(rawSelection, options) {
581
+ const selection = rawSelection.trim();
582
+ if (!selection)
583
+ return null;
584
+ const number = Number(selection);
585
+ if (Number.isInteger(number) && number >= 1 && number <= options.length) {
586
+ return options[number - 1].name;
587
+ }
588
+ const normalized = selection.toLowerCase();
589
+ return options.find((option) => option.name.toLowerCase() === normalized)?.name ?? null;
590
+ }
447
591
  async function resolveLoginKey(args, io) {
448
592
  if (args.key?.trim())
449
593
  return args.key.trim();
@@ -460,12 +604,14 @@ async function resolveLoginKey(args, io) {
460
604
  }
461
605
  return null;
462
606
  }
463
- function resolveRequest(io) {
607
+ function resolveRequest(io, apiKeyOverride) {
464
608
  if (io.request)
465
609
  return io.request;
466
- const apiKey = resolveApiKey();
610
+ const apiKey = apiKeyOverride?.trim() || resolveApiKey();
467
611
  if (!apiKey)
468
612
  return null;
613
+ if (io.createRequestFromApiKey)
614
+ return io.createRequestFromApiKey(apiKey);
469
615
  const client = new ApiClient(apiKey);
470
616
  return (method, path, params) => client.request(method, path, params);
471
617
  }
@@ -508,6 +654,9 @@ async function resolveToolArgs(args, tool, io) {
508
654
  if (storageAnswer === null)
509
655
  return null;
510
656
  await resolveStorageDestination(resolved, tool.name, io);
657
+ const filterAnswer = await resolveInteractiveFilter(resolved, io);
658
+ if (filterAnswer === null)
659
+ return null;
511
660
  }
512
661
  else if (missingToolArgs(resolved, tool).length > 0) {
513
662
  return null;
@@ -528,6 +677,29 @@ async function resolveInteractiveStorageMode(resolved, toolName, io) {
528
677
  function storeModeSchema(toolName) {
529
678
  return supportsCsvStorage(toolName) ? CSV_STORE_SCHEMA : JSON_STORE_SCHEMA;
530
679
  }
680
+ async function resolveInteractiveFilter(resolved, io) {
681
+ if (resolved.filter !== undefined)
682
+ return;
683
+ const answer = await promptForOptionalFilterArg(io);
684
+ if (answer === null)
685
+ return null;
686
+ if (answer !== undefined)
687
+ resolved.filter = answer;
688
+ }
689
+ async function promptForOptionalFilterArg(io) {
690
+ const label = "Filter";
691
+ const question = optionalPromptQuestion(label, FILTER_SCHEMA);
692
+ for (let attempt = 0; attempt < MAX_INTERACTIVE_PROMPT_ATTEMPTS; attempt++) {
693
+ const answer = (await io.prompt?.(question))?.trim() ?? "";
694
+ if (!answer)
695
+ return undefined;
696
+ const error = validateFilterExpression(answer);
697
+ if (!error)
698
+ return answer;
699
+ io.write(`Invalid ${label}: ${error}\n`);
700
+ }
701
+ return null;
702
+ }
531
703
  function shouldSkipInteractiveToolArg(toolName, key, args) {
532
704
  if (toolName === "screen_crypto" && key === "countries")
533
705
  return true;
@@ -640,7 +812,7 @@ async function resolveDownloadHistoryArgs(args, io) {
640
812
  if (resolved[key] !== undefined)
641
813
  continue;
642
814
  if (REQUIRED_DOWNLOAD_HISTORY_ARGS.includes(key)) {
643
- const answer = await promptForDownloadHistoryArg(key, io);
815
+ const answer = await promptForDownloadHistoryArg(key, resolved, io);
644
816
  if (!answer)
645
817
  return null;
646
818
  resolved[key] = answer;
@@ -786,11 +958,18 @@ function validateStoreMode(toolName, store) {
786
958
  }
787
959
  return null;
788
960
  }
789
- async function promptForDownloadHistoryArg(key, io) {
961
+ async function promptForDownloadHistoryArg(key, resolved, io) {
790
962
  const label = downloadHistoryPromptLabel(key);
791
- const question = promptQuestion(label, downloadHistorySchema[key], "required");
963
+ const defaultAnswer = getDownloadHistoryPromptDefault(key, resolved);
964
+ const question = defaultAnswer
965
+ ? promptQuestion(label, downloadHistorySchema[key], "required", {
966
+ blankInstruction: "press Enter to use default",
967
+ value: defaultAnswer,
968
+ })
969
+ : promptQuestion(label, downloadHistorySchema[key], "required");
792
970
  for (let attempt = 0; attempt < MAX_INTERACTIVE_PROMPT_ATTEMPTS; attempt++) {
793
- const answer = (await io.prompt?.(question))?.trim() ?? "";
971
+ const rawAnswer = (await io.prompt?.(question))?.trim() ?? "";
972
+ const answer = rawAnswer || defaultAnswer || "";
794
973
  const error = validateDownloadHistoryArgAnswer(key, answer);
795
974
  if (error) {
796
975
  io.write(`Invalid ${label}: ${error}\n`);
@@ -803,6 +982,20 @@ async function promptForDownloadHistoryArg(key, io) {
803
982
  }
804
983
  return null;
805
984
  }
985
+ function getDownloadHistoryPromptDefault(key, resolved) {
986
+ if (key !== "to")
987
+ return null;
988
+ return resolved.bar_type === "second" ? currentUtcDay() : currentUtcMonth();
989
+ }
990
+ function currentUtcDay(date = new Date()) {
991
+ return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())}`;
992
+ }
993
+ function currentUtcMonth(date = new Date()) {
994
+ return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}`;
995
+ }
996
+ function pad2(value) {
997
+ return String(value).padStart(2, "0");
998
+ }
806
999
  async function validateDownloadHistoryStorageAnswer(key, answer) {
807
1000
  if (key !== "output_dir")
808
1001
  return null;
@@ -859,6 +1052,22 @@ function validateDownloadHistoryArgs(args) {
859
1052
  return historyIntervalError;
860
1053
  return null;
861
1054
  }
1055
+ function collectInvalidProvidedDownloadHistoryArgs(args) {
1056
+ const errors = [];
1057
+ for (const key of Object.keys(downloadHistorySchema)) {
1058
+ if (args[key] === undefined)
1059
+ continue;
1060
+ const error = validateDownloadHistoryArgAnswer(key, args[key]);
1061
+ if (error)
1062
+ errors.push({ key, error });
1063
+ }
1064
+ if (!errors.some((error) => error.key === "bar_interval")) {
1065
+ const historyIntervalError = validateHistoryIntervalArgs(DOWNLOAD_HISTORY_COMMAND, args);
1066
+ if (historyIntervalError)
1067
+ errors.push(historyIntervalError);
1068
+ }
1069
+ return errors;
1070
+ }
862
1071
  function downloadHistoryPromptLabel(key) {
863
1072
  switch (key) {
864
1073
  case "symbol":
@@ -919,7 +1128,7 @@ function parseDownloadHistoryArgs(args) {
919
1128
  function optionalPromptQuestion(label, zodType) {
920
1129
  return promptQuestion(label, zodType, "optional");
921
1130
  }
922
- function promptQuestion(label, zodType, requirement) {
1131
+ function promptQuestion(label, zodType, requirement, defaultOverride) {
923
1132
  const parts = [requirement];
924
1133
  const enumValues = getZodEnumValues(zodType);
925
1134
  if (enumValues.length > 0)
@@ -929,12 +1138,16 @@ function promptQuestion(label, zodType, requirement) {
929
1138
  if (typeName && typeName !== "string")
930
1139
  parts.push(`type: ${typeName}`);
931
1140
  }
932
- const defaultValue = getPromptDefault(zodType);
1141
+ const defaultValue = defaultOverride?.value ?? getPromptDefault(zodType);
933
1142
  if (defaultValue !== null)
934
1143
  parts.push(`Default: ${defaultValue}`);
935
1144
  const hint = getPromptHint(zodType);
936
- if (requirement !== "required")
1145
+ if (defaultOverride) {
1146
+ parts.push(defaultOverride.blankInstruction);
1147
+ }
1148
+ else if (requirement !== "required") {
937
1149
  parts.push("press Enter to skip");
1150
+ }
938
1151
  const description = hint ? `${label}: ${hint}\n` : "";
939
1152
  return `${description}${label} (${parts.join(", ")}): `;
940
1153
  }
@@ -975,13 +1188,114 @@ function getPromptHint(zodType) {
975
1188
  return null;
976
1189
  return hint;
977
1190
  }
1191
+ function canUseKeyboardToolSelection() {
1192
+ return (input.isTTY === true &&
1193
+ output.isTTY === true &&
1194
+ typeof input.setRawMode === "function" &&
1195
+ process.env.CI !== "true");
1196
+ }
1197
+ function selectToolWithKeyboard(options) {
1198
+ return new Promise((resolve) => {
1199
+ let selectedIndex = 0;
1200
+ let typed = "";
1201
+ let error = "";
1202
+ let closed = false;
1203
+ const wasRaw = input.isRaw;
1204
+ const render = () => {
1205
+ output.write("\x1B[2J\x1B[H\x1B[?25l");
1206
+ output.write("Choose a tool:\n\n");
1207
+ for (const [index, option] of options.entries()) {
1208
+ const marker = index === selectedIndex ? ">" : " ";
1209
+ output.write(`${marker} ${index + 1}. ${option.name} - ${option.description}\n`);
1210
+ }
1211
+ output.write("\nUse Up/Down, Enter, or type a number/name.");
1212
+ output.write(`\nChoose tool: ${typed}`);
1213
+ if (error)
1214
+ output.write(`\n${error}`);
1215
+ };
1216
+ const cleanup = () => {
1217
+ if (closed)
1218
+ return;
1219
+ closed = true;
1220
+ input.off("keypress", onKeypress);
1221
+ input.setRawMode(Boolean(wasRaw));
1222
+ output.write("\x1B[?25h\n");
1223
+ };
1224
+ const finish = (toolName) => {
1225
+ cleanup();
1226
+ resolve(toolName);
1227
+ };
1228
+ const updateSelectedFromTyped = () => {
1229
+ const selected = parseToolSelection(typed, options);
1230
+ if (!selected)
1231
+ return;
1232
+ selectedIndex = options.findIndex((option) => option.name === selected);
1233
+ };
1234
+ const onKeypress = (value, key) => {
1235
+ if (key.ctrl && key.name === "c") {
1236
+ finish(null);
1237
+ return;
1238
+ }
1239
+ if (key.name === "escape") {
1240
+ finish(null);
1241
+ return;
1242
+ }
1243
+ if (key.name === "up") {
1244
+ selectedIndex = selectedIndex === 0 ? options.length - 1 : selectedIndex - 1;
1245
+ typed = "";
1246
+ error = "";
1247
+ render();
1248
+ return;
1249
+ }
1250
+ if (key.name === "down") {
1251
+ selectedIndex = selectedIndex === options.length - 1 ? 0 : selectedIndex + 1;
1252
+ typed = "";
1253
+ error = "";
1254
+ render();
1255
+ return;
1256
+ }
1257
+ if (key.name === "return" || key.name === "enter") {
1258
+ const selected = typed.trim()
1259
+ ? parseToolSelection(typed, options)
1260
+ : options[selectedIndex].name;
1261
+ if (selected) {
1262
+ finish(selected);
1263
+ return;
1264
+ }
1265
+ error = "Invalid tool selection.";
1266
+ render();
1267
+ return;
1268
+ }
1269
+ if (key.name === "backspace") {
1270
+ typed = typed.slice(0, -1);
1271
+ error = "";
1272
+ updateSelectedFromTyped();
1273
+ render();
1274
+ return;
1275
+ }
1276
+ if (value && value >= " " && !key.ctrl && key.sequence !== "\x7F") {
1277
+ typed += value;
1278
+ error = "";
1279
+ updateSelectedFromTyped();
1280
+ render();
1281
+ }
1282
+ };
1283
+ emitKeypressEvents(input);
1284
+ input.setRawMode(true);
1285
+ input.resume();
1286
+ input.on("keypress", onKeypress);
1287
+ render();
1288
+ });
1289
+ }
978
1290
  export function main() {
979
1291
  const rl = createInterface({ input, output });
1292
+ const interactive = process.stdin.isTTY && process.stdout.isTTY && process.env.CI !== "true";
980
1293
  runCli(process.argv.slice(2), {
981
1294
  write: (s) => process.stdout.write(`${s}\n`),
982
1295
  progress: (s) => process.stderr.write(`${s}\n`),
983
1296
  prompt: (question) => rl.question(question),
984
- isInteractive: process.stdin.isTTY && process.stdout.isTTY && process.env.CI !== "true",
1297
+ selectTool: canUseKeyboardToolSelection() ? selectToolWithKeyboard : undefined,
1298
+ isInteractive: interactive,
985
1299
  exit: (code) => process.exit(code),
986
1300
  }).finally(() => rl.close());
987
1301
  }