@trading-boy/cli 1.7.0 → 1.9.0

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.
@@ -56362,7 +56362,7 @@ function registerConfigCommand(program2) {
56362
56362
  process.exitCode = 1;
56363
56363
  }
56364
56364
  });
56365
- configCmd.command("set-llm-key <apiKey>").description("Store your LLM API key for thesis extraction + coaching (BYOK)").addOption(new Option("-p, --provider <provider>", "LLM provider (auto-detected from key prefix if omitted)").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "custom"])).option("-m, --model <model>", "Model name (default for all phases)").option("--base-url <url>", "Custom base URL (for openrouter/ollama/custom providers)").option("--scan-model <model>", "Model for market scanning (e.g. claude-haiku-4-5)").option("--analyze-model <model>", "Model for deep analysis (e.g. claude-sonnet-4-6)").option("--decide-model <model>", "Model for trade decisions (e.g. claude-opus-4-6)").action(async (apiKey, opts) => {
56365
+ configCmd.command("set-llm-key <apiKey>").description("Store your LLM API key for thesis extraction + coaching (BYOK)").addOption(new Option("-p, --provider <provider>", "LLM provider (auto-detected from key prefix if omitted)").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "custom"])).option("-m, --model <model>", "Model name (default for all phases)").option("--base-url <url>", "Custom base URL (for openrouter/ollama/custom providers)").option("--scan-model <model>", "Model for market scanning (e.g. claude-haiku-4-5)").option("--analyze-model <model>", "Model for deep analysis (e.g. claude-sonnet-4-6)").option("--decide-model <model>", "Model for trade decisions (e.g. claude-opus-4-6)").addOption(new Option("--scan-provider <provider>", "Provider for scan phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "custom"])).option("--scan-key <key>", "API key for scan phase provider").addOption(new Option("--analyze-provider <provider>", "Provider for analyze phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "custom"])).option("--analyze-key <key>", "API key for analyze phase provider").addOption(new Option("--decide-provider <provider>", "Provider for decide phase").choices(["anthropic", "openai", "openrouter", "ollama", "gemini", "custom"])).option("--decide-key <key>", "API key for decide phase provider").action(async (apiKey, opts) => {
56366
56366
  try {
56367
56367
  const result = await apiRequest("/api/v1/llm-config", {
56368
56368
  method: "PUT",
@@ -56373,23 +56373,29 @@ function registerConfigCommand(program2) {
56373
56373
  ...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
56374
56374
  ...opts.scanModel ? { scanModel: opts.scanModel } : {},
56375
56375
  ...opts.analyzeModel ? { analyzeModel: opts.analyzeModel } : {},
56376
- ...opts.decideModel ? { decideModel: opts.decideModel } : {}
56376
+ ...opts.decideModel ? { decideModel: opts.decideModel } : {},
56377
+ ...opts.scanProvider ? { scanProvider: opts.scanProvider } : {},
56378
+ ...opts.scanKey ? { scanApiKey: opts.scanKey } : {},
56379
+ ...opts.analyzeProvider ? { analyzeProvider: opts.analyzeProvider } : {},
56380
+ ...opts.analyzeKey ? { analyzeApiKey: opts.analyzeKey } : {},
56381
+ ...opts.decideProvider ? { decideProvider: opts.decideProvider } : {},
56382
+ ...opts.decideKey ? { decideApiKey: opts.decideKey } : {}
56377
56383
  }
56378
56384
  });
56379
56385
  console.log("");
56380
56386
  console.log(source_default.green(" LLM API key saved successfully"));
56381
- console.log(` ${source_default.gray("Provider:")} ${result.provider}`);
56382
- console.log(` ${source_default.gray("Model:")} ${result.model}`);
56383
- if (result.scanModel) {
56384
- console.log(` ${source_default.gray("Scan model:")} ${result.scanModel}`);
56387
+ console.log(` ${source_default.gray("Provider:")} ${result.provider}`);
56388
+ console.log(` ${source_default.gray("Model:")} ${result.model}`);
56389
+ if (result.scanProvider || result.scanModel) {
56390
+ console.log(` ${source_default.gray("Scan:")} ${result.scanProvider ?? result.provider} / ${result.scanModel ?? result.model}${opts.scanKey ? " (own key)" : ""}`);
56385
56391
  }
56386
- if (result.analyzeModel) {
56387
- console.log(` ${source_default.gray("Analyze model:")} ${result.analyzeModel}`);
56392
+ if (result.analyzeProvider || result.analyzeModel) {
56393
+ console.log(` ${source_default.gray("Analyze:")} ${result.analyzeProvider ?? result.provider} / ${result.analyzeModel ?? result.model}${opts.analyzeKey ? " (own key)" : ""}`);
56388
56394
  }
56389
- if (result.decideModel) {
56390
- console.log(` ${source_default.gray("Decide model:")} ${result.decideModel}`);
56395
+ if (result.decideProvider || result.decideModel) {
56396
+ console.log(` ${source_default.gray("Decide:")} ${result.decideProvider ?? result.provider} / ${result.decideModel ?? result.model}${opts.decideKey ? " (own key)" : ""}`);
56391
56397
  }
56392
- console.log(` ${source_default.gray("Key:")} ${apiKey.slice(0, 8)}${"*".repeat(Math.max(0, apiKey.length - 8))}`);
56398
+ console.log(` ${source_default.gray("Key:")} ${apiKey.slice(0, 8)}${"*".repeat(Math.max(0, apiKey.length - 8))}`);
56393
56399
  console.log("");
56394
56400
  console.log(source_default.dim(" Your key is encrypted at rest. Thesis extraction and coaching are now enabled."));
56395
56401
  console.log("");
@@ -56411,14 +56417,14 @@ function registerConfigCommand(program2) {
56411
56417
  console.log(source_default.gray(" " + "\u2500".repeat(40)));
56412
56418
  console.log(` ${source_default.gray("Provider:")} ${result.provider}`);
56413
56419
  console.log(` ${source_default.gray("Model:")} ${result.model}`);
56414
- if (result.scanModel) {
56415
- console.log(` ${source_default.gray("Scan model:")} ${result.scanModel}`);
56420
+ if (result.scanProvider || result.scanModel) {
56421
+ console.log(` ${source_default.gray("Scan:")} ${result.scanProvider ?? result.provider} / ${result.scanModel ?? result.model}`);
56416
56422
  }
56417
- if (result.analyzeModel) {
56418
- console.log(` ${source_default.gray("Analyze model:")} ${result.analyzeModel}`);
56423
+ if (result.analyzeProvider || result.analyzeModel) {
56424
+ console.log(` ${source_default.gray("Analyze:")} ${result.analyzeProvider ?? result.provider} / ${result.analyzeModel ?? result.model}`);
56419
56425
  }
56420
- if (result.decideModel) {
56421
- console.log(` ${source_default.gray("Decide model:")} ${result.decideModel}`);
56426
+ if (result.decideProvider || result.decideModel) {
56427
+ console.log(` ${source_default.gray("Decide:")} ${result.decideProvider ?? result.provider} / ${result.decideModel ?? result.model}`);
56422
56428
  }
56423
56429
  if (result.baseUrl) {
56424
56430
  console.log(` ${source_default.gray("Base URL:")} ${result.baseUrl}`);
@@ -59155,7 +59161,7 @@ function parseHumanInterval(value) {
59155
59161
  var MIN_SCAN_INTERVAL_MS = 6e4;
59156
59162
  function registerAgentCommand(program2) {
59157
59163
  const agent = program2.command("agent").description("Manage autonomous trading agents");
59158
- agent.command("create").description("Create a new agent").option("--trader-id <traderId>", "Trader ID").option("--strategy-id <strategyId>", "Strategy ID").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS", "OBSERVE_ONLY").option("--scan-interval <ms>", "Scan interval in ms (min 60000)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades", "10").option("--max-daily-loss <usd>", "Max daily loss in USD", "500").option("--max-position-size <pct>", "Max position size as decimal (0.10 = 10%)", "0.10").option("--min-confidence <n>", "Min confidence threshold (0-1)", "0.60").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"]).default("crypto")).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
59164
+ agent.command("create").description("Create a new agent").option("--trader-id <traderId>", "Trader ID").option("--strategy-id <strategyId>", "Strategy ID").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level: OBSERVE_ONLY, SUGGEST, AUTO_WITH_APPROVAL, FULLY_AUTONOMOUS", "OBSERVE_ONLY").option("--scan-interval <ms>", "Scan interval in ms (min 60000)", "300000").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades", "10").option("--max-daily-loss <usd>", "Max daily loss in USD", "500").option("--max-position-size <pct>", "Max position size as decimal (0.10 = 10%)", "0.10").option("--min-confidence <n>", "Min confidence threshold (0-1)", "0.60").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"]).default("crypto")).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").option("--exit-reasoner", "Enable LLM-powered exit reasoning for this agent").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (options) => {
59159
59165
  if (!await ensureRemote())
59160
59166
  return;
59161
59167
  if (!options.traderId) {
@@ -59212,6 +59218,8 @@ function registerAgentCommand(program2) {
59212
59218
  body.decideModel = options.decideModel;
59213
59219
  if (options.assetClass)
59214
59220
  body.assetClass = options.assetClass;
59221
+ if (options.exitReasoner)
59222
+ body.exitReasoner = true;
59215
59223
  if (options.soulFile) {
59216
59224
  const path5 = resolve2(options.soulFile);
59217
59225
  if (!existsSync3(path5)) {
@@ -59440,12 +59448,14 @@ function registerAgentCommand(program2) {
59440
59448
  handleApiError(error49, "Position exit failed", logger30);
59441
59449
  }
59442
59450
  });
59443
- agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level").option("--scan-interval <ms>", "Scan interval in ms").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades").option("--max-daily-loss <usd>", "Max daily loss in USD").option("--max-position-size <pct>", "Max position size as decimal").option("--min-confidence <n>", "Min confidence threshold").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"])).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59451
+ agent.command("update <agentId>").description("Update agent config").option("--name <name>", "Agent name").option("--autonomy <level>", "Autonomy level").option("--scan-interval <ms>", "Scan interval in ms").option("--scan-interval-human <duration>", "Scan interval in human-readable format (e.g. 1m, 5m, 15m, 30m, 1h)").option("--watchlist <symbols>", "Comma-separated token symbols").option("--max-daily-trades <n>", "Max daily trades").option("--max-daily-loss <usd>", "Max daily loss in USD").option("--max-position-size <pct>", "Max position size as decimal").option("--min-confidence <n>", "Min confidence threshold").option("--scan-model <model>", "LLM model for market scanning").option("--analyze-model <model>", "LLM model for deep analysis").option("--decide-model <model>", "LLM model for trade decisions").addOption(new Option("--asset-class <class>", "Asset class for this agent").choices(["crypto", "commodities", "mixed"])).option("--soul-override <text>", "Custom soul/personality for this agent").option("--purpose-override <text>", "Custom purpose/mission for this agent").option("--soul-file <path>", "Load soul from a file").option("--purpose-file <path>", "Load purpose from a file").option("--exit-reasoner", "Enable LLM-powered exit reasoning for this agent").addOption(new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")).action(async (agentId, options) => {
59444
59452
  if (!await ensureRemote())
59445
59453
  return;
59446
59454
  const body = {};
59447
59455
  if (options.name)
59448
59456
  body.name = options.name;
59457
+ if (options.exitReasoner)
59458
+ body.exitReasoner = true;
59449
59459
  if (options.autonomy)
59450
59460
  body.autonomyLevel = options.autonomy;
59451
59461
  if (options.scanIntervalHuman) {
@@ -97,6 +97,7 @@ export function registerAgentCommand(program) {
97
97
  .option('--purpose-override <text>', 'Custom purpose/mission for this agent')
98
98
  .option('--soul-file <path>', 'Load soul from a file')
99
99
  .option('--purpose-file <path>', 'Load purpose from a file')
100
+ .option('--exit-reasoner', 'Enable LLM-powered exit reasoning for this agent')
100
101
  .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
101
102
  .action(async (options) => {
102
103
  if (!(await ensureRemote()))
@@ -157,6 +158,8 @@ export function registerAgentCommand(program) {
157
158
  body.decideModel = options.decideModel;
158
159
  if (options.assetClass)
159
160
  body.assetClass = options.assetClass;
161
+ if (options.exitReasoner)
162
+ body.exitReasoner = true;
160
163
  // Soul override — file takes precedence over inline text
161
164
  if (options.soulFile) {
162
165
  const path = resolve(options.soulFile);
@@ -470,6 +473,7 @@ export function registerAgentCommand(program) {
470
473
  .option('--purpose-override <text>', 'Custom purpose/mission for this agent')
471
474
  .option('--soul-file <path>', 'Load soul from a file')
472
475
  .option('--purpose-file <path>', 'Load purpose from a file')
476
+ .option('--exit-reasoner', 'Enable LLM-powered exit reasoning for this agent')
473
477
  .addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
474
478
  .action(async (agentId, options) => {
475
479
  if (!(await ensureRemote()))
@@ -477,6 +481,8 @@ export function registerAgentCommand(program) {
477
481
  const body = {};
478
482
  if (options.name)
479
483
  body.name = options.name;
484
+ if (options.exitReasoner)
485
+ body.exitReasoner = true;
480
486
  if (options.autonomy)
481
487
  body.autonomyLevel = options.autonomy;
482
488
  // Resolve scan interval: --scan-interval-human takes precedence
@@ -328,6 +328,12 @@ export function registerConfigCommand(program) {
328
328
  .option('--scan-model <model>', 'Model for market scanning (e.g. claude-haiku-4-5)')
329
329
  .option('--analyze-model <model>', 'Model for deep analysis (e.g. claude-sonnet-4-6)')
330
330
  .option('--decide-model <model>', 'Model for trade decisions (e.g. claude-opus-4-6)')
331
+ .addOption(new Option('--scan-provider <provider>', 'Provider for scan phase').choices(['anthropic', 'openai', 'openrouter', 'ollama', 'gemini', 'custom']))
332
+ .option('--scan-key <key>', 'API key for scan phase provider')
333
+ .addOption(new Option('--analyze-provider <provider>', 'Provider for analyze phase').choices(['anthropic', 'openai', 'openrouter', 'ollama', 'gemini', 'custom']))
334
+ .option('--analyze-key <key>', 'API key for analyze phase provider')
335
+ .addOption(new Option('--decide-provider <provider>', 'Provider for decide phase').choices(['anthropic', 'openai', 'openrouter', 'ollama', 'gemini', 'custom']))
336
+ .option('--decide-key <key>', 'API key for decide phase provider')
331
337
  .action(async (apiKey, opts) => {
332
338
  try {
333
339
  const result = await apiRequest('/api/v1/llm-config', {
@@ -340,22 +346,28 @@ export function registerConfigCommand(program) {
340
346
  ...(opts.scanModel ? { scanModel: opts.scanModel } : {}),
341
347
  ...(opts.analyzeModel ? { analyzeModel: opts.analyzeModel } : {}),
342
348
  ...(opts.decideModel ? { decideModel: opts.decideModel } : {}),
349
+ ...(opts.scanProvider ? { scanProvider: opts.scanProvider } : {}),
350
+ ...(opts.scanKey ? { scanApiKey: opts.scanKey } : {}),
351
+ ...(opts.analyzeProvider ? { analyzeProvider: opts.analyzeProvider } : {}),
352
+ ...(opts.analyzeKey ? { analyzeApiKey: opts.analyzeKey } : {}),
353
+ ...(opts.decideProvider ? { decideProvider: opts.decideProvider } : {}),
354
+ ...(opts.decideKey ? { decideApiKey: opts.decideKey } : {}),
343
355
  },
344
356
  });
345
357
  console.log('');
346
358
  console.log(chalk.green(' LLM API key saved successfully'));
347
- console.log(` ${chalk.gray('Provider:')} ${result.provider}`);
348
- console.log(` ${chalk.gray('Model:')} ${result.model}`);
349
- if (result.scanModel) {
350
- console.log(` ${chalk.gray('Scan model:')} ${result.scanModel}`);
359
+ console.log(` ${chalk.gray('Provider:')} ${result.provider}`);
360
+ console.log(` ${chalk.gray('Model:')} ${result.model}`);
361
+ if (result.scanProvider || result.scanModel) {
362
+ console.log(` ${chalk.gray('Scan:')} ${result.scanProvider ?? result.provider} / ${result.scanModel ?? result.model}${opts.scanKey ? ' (own key)' : ''}`);
351
363
  }
352
- if (result.analyzeModel) {
353
- console.log(` ${chalk.gray('Analyze model:')} ${result.analyzeModel}`);
364
+ if (result.analyzeProvider || result.analyzeModel) {
365
+ console.log(` ${chalk.gray('Analyze:')} ${result.analyzeProvider ?? result.provider} / ${result.analyzeModel ?? result.model}${opts.analyzeKey ? ' (own key)' : ''}`);
354
366
  }
355
- if (result.decideModel) {
356
- console.log(` ${chalk.gray('Decide model:')} ${result.decideModel}`);
367
+ if (result.decideProvider || result.decideModel) {
368
+ console.log(` ${chalk.gray('Decide:')} ${result.decideProvider ?? result.provider} / ${result.decideModel ?? result.model}${opts.decideKey ? ' (own key)' : ''}`);
357
369
  }
358
- console.log(` ${chalk.gray('Key:')} ${apiKey.slice(0, 8)}${'*'.repeat(Math.max(0, apiKey.length - 8))}`);
370
+ console.log(` ${chalk.gray('Key:')} ${apiKey.slice(0, 8)}${'*'.repeat(Math.max(0, apiKey.length - 8))}`);
359
371
  console.log('');
360
372
  console.log(chalk.dim(' Your key is encrypted at rest. Thesis extraction and coaching are now enabled.'));
361
373
  console.log('');
@@ -384,14 +396,14 @@ export function registerConfigCommand(program) {
384
396
  console.log(chalk.gray(' ' + '\u2500'.repeat(40)));
385
397
  console.log(` ${chalk.gray('Provider:')} ${result.provider}`);
386
398
  console.log(` ${chalk.gray('Model:')} ${result.model}`);
387
- if (result.scanModel) {
388
- console.log(` ${chalk.gray('Scan model:')} ${result.scanModel}`);
399
+ if (result.scanProvider || result.scanModel) {
400
+ console.log(` ${chalk.gray('Scan:')} ${result.scanProvider ?? result.provider} / ${result.scanModel ?? result.model}`);
389
401
  }
390
- if (result.analyzeModel) {
391
- console.log(` ${chalk.gray('Analyze model:')} ${result.analyzeModel}`);
402
+ if (result.analyzeProvider || result.analyzeModel) {
403
+ console.log(` ${chalk.gray('Analyze:')} ${result.analyzeProvider ?? result.provider} / ${result.analyzeModel ?? result.model}`);
392
404
  }
393
- if (result.decideModel) {
394
- console.log(` ${chalk.gray('Decide model:')} ${result.decideModel}`);
405
+ if (result.decideProvider || result.decideModel) {
406
+ console.log(` ${chalk.gray('Decide:')} ${result.decideProvider ?? result.provider} / ${result.decideModel ?? result.model}`);
395
407
  }
396
408
  if (result.baseUrl) {
397
409
  console.log(` ${chalk.gray('Base URL:')} ${result.baseUrl}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trading-boy/cli",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Trading Boy CLI — crypto context intelligence for traders and AI agents. Query real-time prices, funding rates, whale activity, and DeFi risk for 100+ Solana tokens and 229 Hyperliquid perpetuals.",
5
5
  "homepage": "https://cabal.ventures",
6
6
  "repository": {