@darksol/terminal 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darksol/terminal",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "DARKSOL Terminal — unified CLI for all DARKSOL services. Market intel, trading, oracle, casino, and more.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -591,62 +591,221 @@ export function cli(argv) {
591
591
  });
592
592
 
593
593
  // ═══════════════════════════════════════
594
- // DASHBOARD (default)
594
+ // DASHBOARD (default) — CHAT-FIRST
595
595
  // ═══════════════════════════════════════
596
596
  program
597
597
  .command('dashboard', { isDefault: true })
598
- .description('Show DARKSOL Terminal dashboard')
598
+ .description('Show DARKSOL Terminal — chat-first interface')
599
599
  .action(async () => {
600
600
  showBanner();
601
601
 
602
- // First-run detection — offer setup wizard
602
+ // First-run detection — force setup
603
603
  const ranSetup = await checkFirstRun();
604
604
  if (ranSetup) return;
605
605
 
606
606
  const cfg = getAllConfig();
607
607
  const wallet = cfg.activeWallet;
608
+ const { hasKey } = await import('./config/keys.js');
609
+ const hasLLM = ['openai', 'anthropic', 'openrouter', 'ollama'].some(s => hasKey(s));
610
+
611
+ // ── Status bar (compact) ──
612
+ const statusParts = [
613
+ wallet ? theme.success(`● ${wallet}`) : theme.dim('○ no wallet'),
614
+ theme.dim(`${cfg.chain}`),
615
+ theme.dim(`${cfg.slippage}% slip`),
616
+ hasLLM ? theme.success('● AI ready') : theme.accent('○ no AI'),
617
+ ];
618
+ console.log(` ${statusParts.join(theme.dim(' │ '))}`);
619
+ console.log('');
620
+
621
+ // ── CHAT INTERFACE (primary) ──
622
+ if (hasLLM) {
623
+ // AI is connected — drop straight into chat
624
+ console.log(theme.gold(' ╔══════════════════════════════════════════════════════════╗'));
625
+ console.log(theme.gold(' ║') + theme.label(' DARKSOL AI — ready. Ask anything. ') + theme.gold('║'));
626
+ console.log(theme.gold(' ║') + theme.dim(' "swap 0.1 ETH for USDC" • "what\'s AERO at?" • "help" ') + theme.gold('║'));
627
+ console.log(theme.gold(' ║') + theme.dim(' Type "commands" to see all tools. Type "exit" to quit. ') + theme.gold('║'));
628
+ console.log(theme.gold(' ╚══════════════════════════════════════════════════════════╝'));
629
+ console.log('');
630
+
631
+ // Start interactive chat loop
632
+ await startChatLoop(cfg);
633
+ } else {
634
+ // No AI — show connect prompt
635
+ console.log(theme.gold(' ╔══════════════════════════════════════════════════════════╗'));
636
+ console.log(theme.gold(' ║') + theme.accent(' ⚠ No AI provider connected ') + theme.gold('║'));
637
+ console.log(theme.gold(' ║') + theme.dim(' The DARKSOL AI needs an LLM to work. ') + theme.gold('║'));
638
+ console.log(theme.gold(' ║') + theme.dim(' ') + theme.gold('║'));
639
+ console.log(theme.gold(' ║') + theme.dim(' Run: ') + theme.label('darksol setup') + theme.dim(' to connect OpenAI/Anthropic ') + theme.gold('║'));
640
+ console.log(theme.gold(' ║') + theme.dim(' Run: ') + theme.label('darksol keys add openai') + theme.dim(' to add an API key ') + theme.gold('║'));
641
+ console.log(theme.gold(' ║') + theme.dim(' ') + theme.gold('║'));
642
+ console.log(theme.gold(' ║') + theme.dim(' Or use Ollama for free local AI — no key needed. ') + theme.gold('║'));
643
+ console.log(theme.gold(' ╚══════════════════════════════════════════════════════════╝'));
644
+ console.log('');
645
+ showCommandList();
646
+ }
647
+ });
608
648
 
609
- showSection('STATUS');
649
+ program.parse(argv);
650
+ }
651
+
652
+ // ═══════════════════════════════════════
653
+ // CHAT-FIRST LOOP (default experience)
654
+ // ═══════════════════════════════════════
655
+
656
+ async function startChatLoop(cfg) {
657
+ const { createLLM } = await import('./llm/engine.js');
658
+ const { quickPrice } = await import('./utils/helpers.js');
659
+ const { executeIntent, parseIntent, INTENT_SYSTEM_PROMPT } = await import('./llm/intent.js');
660
+
661
+ let llm;
662
+ try {
663
+ llm = await createLLM({});
664
+ const chain = cfg.chain || 'base';
665
+ const wallet = cfg.activeWallet || '(not set)';
666
+ const slippage = cfg.slippage || 0.5;
667
+
668
+ const systemPrompt = INTENT_SYSTEM_PROMPT
669
+ .replace('{{chain}}', chain)
670
+ .replace('{{wallet}}', wallet)
671
+ .replace('{{slippage}}', slippage);
672
+
673
+ llm.setSystemPrompt(systemPrompt);
674
+ } catch (err) {
675
+ error(`AI init failed: ${err.message}`);
676
+ info('Run: darksol setup');
677
+ return;
678
+ }
679
+
680
+ const inquirerMod = await import('inquirer');
681
+ const inquirerDefault = inquirerMod.default;
682
+
683
+ while (true) {
684
+ const { input } = await inquirerDefault.prompt([{
685
+ type: 'input',
686
+ name: 'input',
687
+ message: theme.gold('🌑'),
688
+ validate: (v) => v.length > 0 || ' ',
689
+ }]);
690
+
691
+ const trimmed = input.trim().toLowerCase();
692
+
693
+ // Meta-commands within chat
694
+ if (['exit', 'quit', 'q'].includes(trimmed)) {
695
+ const usage = llm.getUsage();
696
+ info(`Session: ${usage.calls} calls, ${usage.totalTokens} tokens`);
697
+ break;
698
+ }
699
+
700
+ if (['commands', 'help', 'cmds', '?'].includes(trimmed)) {
701
+ showCommandList();
702
+ continue;
703
+ }
704
+
705
+ if (trimmed === 'status') {
610
706
  kvDisplay([
611
- ['Wallet', wallet || theme.dim('Not set — run: darksol wallet create')],
707
+ ['Wallet', cfg.activeWallet || theme.dim('(none)')],
612
708
  ['Chain', cfg.chain],
709
+ ['Provider', `${llm.provider}/${llm.model}`],
613
710
  ['Slippage', `${cfg.slippage}%`],
614
711
  ]);
712
+ continue;
713
+ }
615
714
 
616
- console.log('');
617
- showSection('COMMANDS');
618
- const commands = [
619
- ['wallet', 'Create, import, manage wallets'],
620
- ['trade', 'Swap tokens, snipe, trading'],
621
- ['dca', 'Dollar-cost averaging orders'],
622
- ['ai', 'AI trading assistant & analysis'],
623
- ['agent', 'Secure agent signer (PK-isolated)'],
624
- ['keys', 'API key vault (LLMs, data, RPCs)'],
625
- ['script', 'Execution scripts & strategies'],
626
- ['market', 'Market intel & token data'],
627
- ['oracle', 'On-chain random oracle'],
628
- ['casino', 'The Clawsino — betting'],
629
- ['cards', 'Prepaid Visa/MC cards'],
630
- ['builders', 'ERC-8021 builder index'],
631
- ['facilitator', 'x402 payment facilitator'],
632
- ['skills', 'Agent skill directory & install'],
633
- ['config', 'Terminal configuration'],
634
- ['tips', 'Trading & scripting tips'],
635
- ['networks', 'Chain reference & explorers'],
636
- ['quickstart', 'Getting started guide'],
637
- ['lookup', 'Look up any address on-chain'],
638
- ['setup', 'First-run setup wizard'],
639
- ];
715
+ // Check if it looks like a trade intent
716
+ const tradeWords = ['buy', 'sell', 'swap', 'snipe', 'transfer', 'send', 'trade'];
717
+ const isTradeIntent = tradeWords.some(w => trimmed.startsWith(w));
718
+
719
+ if (isTradeIntent) {
720
+ // Parse as trade intent with confirmation
721
+ const intent = await parseIntent(input, {});
722
+ if (intent.action !== 'error' && intent.action !== 'unknown') {
723
+ showSection('PARSED INTENT');
724
+ kvDisplay(Object.entries(intent)
725
+ .filter(([k]) => !['raw', 'model', 'reasoning'].includes(k))
726
+ .map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : String(v)])
727
+ );
728
+ if (intent.warnings?.length) intent.warnings.forEach(w => warn(w));
729
+ if (intent.command) info(`Command: ${theme.gold(intent.command)}`);
730
+ console.log('');
731
+ } else {
732
+ // Fall through to regular chat
733
+ await chatResponse(llm, input);
734
+ }
735
+ continue;
736
+ }
640
737
 
641
- commands.forEach(([cmd, desc]) => {
642
- console.log(` ${theme.gold(cmd.padEnd(16))} ${theme.dim(desc)}`);
643
- });
738
+ // Regular chat
739
+ await chatResponse(llm, input);
740
+ }
741
+ }
644
742
 
645
- console.log('');
646
- console.log(theme.dim(' Run any command with --help for details'));
647
- console.log(theme.dim(' Example: darksol trade swap --help'));
648
- console.log('');
649
- });
743
+ async function chatResponse(llm, input) {
744
+ const { quickPrice } = await import('./utils/helpers.js');
745
+ const { spinner: spin } = await import('./ui/components.js');
746
+ const s = spin('Thinking...').start();
747
+
748
+ try {
749
+ // Enrich with live price data
750
+ let enriched = input;
751
+ const tokenPattern = /\b([A-Z]{2,10})\b/g;
752
+ const tokens = [...new Set(input.toUpperCase().match(tokenPattern) || [])];
753
+ const skipTokens = ['ETH', 'THE', 'FOR', 'AND', 'BUY', 'SELL', 'DCA', 'SWAP', 'WHAT', 'PRICE', 'HOW', 'MUCH', 'NOT', 'CAN', 'YOU', 'HELP'];
754
+
755
+ const priceData = [];
756
+ for (const t of tokens.filter(t => !skipTokens.includes(t)).slice(0, 3)) {
757
+ const p = await quickPrice(t);
758
+ if (p) priceData.push(`${p.symbol}: $${p.price} (liq: $${p.liquidity})`);
759
+ }
760
+
761
+ if (priceData.length > 0) {
762
+ enriched += `\n\n[Live data: ${priceData.join(', ')}]`;
763
+ }
764
+
765
+ const result = await llm.chat(enriched);
766
+ s.succeed('');
767
+
768
+ // Display response
769
+ console.log('');
770
+ const lines = result.content.split('\n');
771
+ for (const line of lines) {
772
+ console.log(theme.dim(' ') + line);
773
+ }
774
+ console.log('');
775
+ } catch (err) {
776
+ s.fail('Error');
777
+ error(err.message);
778
+ }
779
+ }
650
780
 
651
- program.parse(argv);
781
+ function showCommandList() {
782
+ console.log('');
783
+ showSection('COMMANDS');
784
+ const commands = [
785
+ ['wallet', 'Create, import, manage wallets'],
786
+ ['trade', 'Swap tokens, snipe, trading'],
787
+ ['dca', 'Dollar-cost averaging orders'],
788
+ ['ai chat', 'Standalone AI chat session'],
789
+ ['ai execute', 'Parse + execute a trade via AI'],
790
+ ['agent start', 'Start secure agent signer'],
791
+ ['keys', 'API key vault'],
792
+ ['script', 'Execution scripts & strategies'],
793
+ ['market', 'Market intel & token data'],
794
+ ['oracle', 'On-chain random oracle'],
795
+ ['casino', 'The Clawsino — betting'],
796
+ ['cards', 'Prepaid Visa/MC cards'],
797
+ ['builders', 'ERC-8021 builder index'],
798
+ ['facilitator', 'x402 payment facilitator'],
799
+ ['skills', 'Agent skill directory'],
800
+ ['setup', 'Re-run setup wizard'],
801
+ ['config', 'Terminal configuration'],
802
+ ];
803
+
804
+ commands.forEach(([cmd, desc]) => {
805
+ console.log(` ${theme.gold(cmd.padEnd(16))} ${theme.dim(desc)}`);
806
+ });
807
+
808
+ console.log('');
809
+ console.log(theme.dim(' Run any command: darksol <command> --help'));
810
+ console.log('');
652
811
  }
@@ -494,23 +494,12 @@ function showPostSetup() {
494
494
  }
495
495
 
496
496
  /**
497
- * Quick check on startup — if first run, prompt setup
497
+ * Quick check on startup — if first run, FORCE setup (no prompt)
498
498
  */
499
499
  export async function checkFirstRun() {
500
500
  if (isFirstRun()) {
501
- console.log('');
502
- warn('No AI provider configured yet.');
503
- const { runSetup } = await inquirer.prompt([{
504
- type: 'confirm',
505
- name: 'runSetup',
506
- message: theme.gold('Run setup wizard?'),
507
- default: true,
508
- }]);
509
- if (runSetup) {
510
- await runSetupWizard();
511
- return true;
512
- }
513
- info('Skip for now. Run later: darksol setup');
501
+ await runSetupWizard();
502
+ return true;
514
503
  }
515
504
  return false;
516
505
  }
package/src/ui/banner.js CHANGED
@@ -26,7 +26,7 @@ export function showBanner(opts = {}) {
26
26
  );
27
27
  console.log(
28
28
  theme.dim(' ║ ') +
29
- theme.subtle(' v0.3.0') +
29
+ theme.subtle(' v0.3.1') +
30
30
  theme.dim(' ') +
31
31
  theme.gold('🌑') +
32
32
  theme.dim(' ║')
@@ -44,7 +44,7 @@ export function showBanner(opts = {}) {
44
44
 
45
45
  export function showMiniBanner() {
46
46
  console.log('');
47
- console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.3.0'));
47
+ console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.3.1'));
48
48
  console.log(theme.dim(' ─────────────────────────────'));
49
49
  console.log('');
50
50
  }
@@ -63,3 +63,4 @@ export function showDivider() {
63
63
 
64
64
 
65
65
 
66
+