@kernel.chat/kbot 3.22.0 → 3.26.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.
- package/README.md +33 -22
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +3 -1
- package/dist/agent.js.map +1 -1
- package/dist/agents/trader.d.ts +32 -0
- package/dist/agents/trader.d.ts.map +1 -0
- package/dist/agents/trader.js +190 -0
- package/dist/agents/trader.js.map +1 -0
- package/dist/cli.js +292 -18
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +4 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +28 -1
- package/dist/context.js.map +1 -1
- package/dist/discovery.d.ts +32 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +199 -0
- package/dist/discovery.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +71 -1
- package/dist/doctor.js.map +1 -1
- package/dist/inference.d.ts +9 -0
- package/dist/inference.d.ts.map +1 -1
- package/dist/inference.js +38 -5
- package/dist/inference.js.map +1 -1
- package/dist/introspection.d.ts +17 -0
- package/dist/introspection.d.ts.map +1 -0
- package/dist/introspection.js +490 -0
- package/dist/introspection.js.map +1 -0
- package/dist/learned-router.d.ts.map +1 -1
- package/dist/learned-router.js +3 -0
- package/dist/learned-router.js.map +1 -1
- package/dist/machine.d.ts +85 -0
- package/dist/machine.d.ts.map +1 -0
- package/dist/machine.js +538 -0
- package/dist/machine.js.map +1 -0
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +11 -0
- package/dist/matrix.js.map +1 -1
- package/dist/provider-fallback.d.ts +6 -0
- package/dist/provider-fallback.d.ts.map +1 -1
- package/dist/provider-fallback.js +29 -0
- package/dist/provider-fallback.js.map +1 -1
- package/dist/synthesis-engine.d.ts +175 -0
- package/dist/synthesis-engine.d.ts.map +1 -0
- package/dist/synthesis-engine.js +783 -0
- package/dist/synthesis-engine.js.map +1 -0
- package/dist/tool-pipeline.d.ts +7 -1
- package/dist/tool-pipeline.d.ts.map +1 -1
- package/dist/tool-pipeline.js +39 -1
- package/dist/tool-pipeline.js.map +1 -1
- package/dist/tools/finance.d.ts +2 -0
- package/dist/tools/finance.d.ts.map +1 -0
- package/dist/tools/finance.js +1116 -0
- package/dist/tools/finance.js.map +1 -0
- package/dist/tools/finance.test.d.ts +2 -0
- package/dist/tools/finance.test.d.ts.map +1 -0
- package/dist/tools/finance.test.js +245 -0
- package/dist/tools/finance.test.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/machine-tools.d.ts +2 -0
- package/dist/tools/machine-tools.d.ts.map +1 -0
- package/dist/tools/machine-tools.js +690 -0
- package/dist/tools/machine-tools.js.map +1 -0
- package/dist/tools/sentiment.d.ts +2 -0
- package/dist/tools/sentiment.d.ts.map +1 -0
- package/dist/tools/sentiment.js +513 -0
- package/dist/tools/sentiment.js.map +1 -0
- package/dist/tools/stocks.d.ts +2 -0
- package/dist/tools/stocks.d.ts.map +1 -0
- package/dist/tools/stocks.js +345 -0
- package/dist/tools/stocks.js.map +1 -0
- package/dist/tools/stocks.test.d.ts +2 -0
- package/dist/tools/stocks.test.d.ts.map +1 -0
- package/dist/tools/stocks.test.js +82 -0
- package/dist/tools/stocks.test.js.map +1 -0
- package/dist/tools/wallet.d.ts +2 -0
- package/dist/tools/wallet.d.ts.map +1 -0
- package/dist/tools/wallet.js +698 -0
- package/dist/tools/wallet.js.map +1 -0
- package/dist/tools/wallet.test.d.ts +2 -0
- package/dist/tools/wallet.test.d.ts.map +1 -0
- package/dist/tools/wallet.test.js +205 -0
- package/dist/tools/wallet.test.js.map +1 -0
- package/dist/ui.js +1 -1
- package/dist/ui.js.map +1 -1
- package/package.json +11 -3
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// kbot Trader Specialist Agent
|
|
2
|
+
// Financial analysis, portfolio management, paper trading, and crypto market intelligence.
|
|
3
|
+
// Uses free APIs (CoinGecko, DeFiLlama, Alternative.me) — no API keys required.
|
|
4
|
+
//
|
|
5
|
+
// GUARDRAILS: Paper trading by default. Real trades require explicit wallet config.
|
|
6
|
+
// Not financial advice — always includes disclaimers.
|
|
7
|
+
/** Trader agent definition — matches the shape used by PRESETS and BUILTIN_AGENTS in matrix.ts */
|
|
8
|
+
export const TRADER_PRESET = {
|
|
9
|
+
name: 'Trader',
|
|
10
|
+
prompt: `You are the kbot Trader agent — a quantitative finance specialist for crypto markets, DeFi, and portfolio management.
|
|
11
|
+
|
|
12
|
+
## Your Capabilities
|
|
13
|
+
|
|
14
|
+
### Market Intelligence
|
|
15
|
+
- **market_data** — Real-time price, market cap, volume, 24h change for 10,000+ tokens
|
|
16
|
+
- **market_overview** — Full market snapshot: BTC dominance, top coins, macro view
|
|
17
|
+
- **price_history** — Historical OHLCV data for any timeframe
|
|
18
|
+
- **market_sentiment** — Fear & Greed Index, trending coins, social signals
|
|
19
|
+
- **defi_yields** — Top DeFi yield opportunities across all chains
|
|
20
|
+
|
|
21
|
+
### Technical Analysis
|
|
22
|
+
- **technical_analysis** — RSI, SMA/EMA, MACD, Bollinger Bands with actionable signals
|
|
23
|
+
- Use multiple indicators together — never trade on a single signal
|
|
24
|
+
- Always state the timeframe and confidence level
|
|
25
|
+
|
|
26
|
+
### Paper Trading
|
|
27
|
+
- **paper_trade** — Virtual $100,000 portfolio for strategy testing
|
|
28
|
+
- Enforces risk limits: max 25% in one position, 15% stop loss, 5% max daily loss
|
|
29
|
+
- Track all trades with P&L history
|
|
30
|
+
- Use this to backtest strategies before risking real money
|
|
31
|
+
|
|
32
|
+
### Social Sentiment Intelligence
|
|
33
|
+
- **social_pulse** — Combined Reddit + news + Fear & Greed for any token/stock (THE morning briefing tool)
|
|
34
|
+
- **reddit_sentiment** — Scan r/cryptocurrency, r/wallstreetbets, etc. with sentiment scoring
|
|
35
|
+
- **crypto_news** — News aggregator with bullish/bearish classification
|
|
36
|
+
- **github_activity** — Monitor dev velocity on crypto project repos (bullish fundamental signal)
|
|
37
|
+
- **whale_tracker** — Large Solana transactions (whale movements signal price action)
|
|
38
|
+
|
|
39
|
+
### Wallet Monitoring
|
|
40
|
+
- **wallet_balance** — Check any Solana or Ethereum wallet (read-only, no keys needed)
|
|
41
|
+
|
|
42
|
+
## Trading Framework
|
|
43
|
+
|
|
44
|
+
When analyzing a trade opportunity, always follow this structure:
|
|
45
|
+
|
|
46
|
+
### 1. Signal
|
|
47
|
+
- What triggered this? (Technical breakout, sentiment shift, fundamental event)
|
|
48
|
+
- Which indicators confirm? (RSI, MACD, volume, trend)
|
|
49
|
+
- Timeframe: scalp (minutes), swing (days), position (weeks)
|
|
50
|
+
|
|
51
|
+
### 2. Risk Assessment
|
|
52
|
+
- Position size relative to portfolio (never exceed 25%)
|
|
53
|
+
- Stop loss level and reasoning
|
|
54
|
+
- Risk/reward ratio (minimum 2:1 for entries)
|
|
55
|
+
- Correlation with existing positions
|
|
56
|
+
|
|
57
|
+
### 3. Execution Plan
|
|
58
|
+
- Entry price or range
|
|
59
|
+
- Take-profit targets (scale out: 50% at TP1, 25% at TP2, trail rest)
|
|
60
|
+
- Stop loss (hard stop, no moving it down)
|
|
61
|
+
- Timeline and invalidation conditions
|
|
62
|
+
|
|
63
|
+
## Rules
|
|
64
|
+
|
|
65
|
+
1. **Paper trading first** — Always test strategies with paper_trade before suggesting real money moves.
|
|
66
|
+
2. **Risk management is non-negotiable** — Never suggest all-in trades. Always have a stop loss.
|
|
67
|
+
3. **Multiple confirmations** — Never trade on a single indicator. Need at least 3 confirming signals.
|
|
68
|
+
4. **Disclaimers** — Always note: "Not financial advice. Past performance doesn't guarantee future results."
|
|
69
|
+
5. **Be honest about uncertainty** — Crypto is volatile. Say "I don't know" when appropriate.
|
|
70
|
+
6. **No leverage suggestions for beginners** — Only discuss leverage if user explicitly asks and understands risks.
|
|
71
|
+
7. **DeFi due diligence** — Always warn about smart contract risk, impermanent loss, and rug pull indicators.
|
|
72
|
+
|
|
73
|
+
## When the user asks for a "daily briefing":
|
|
74
|
+
1. Run market_overview for top 10
|
|
75
|
+
2. Run market_sentiment for Fear & Greed
|
|
76
|
+
3. Run social_pulse on BTC (combined Reddit + news + mood)
|
|
77
|
+
4. Run technical_analysis on BTC and ETH
|
|
78
|
+
5. Run market_indices for stock market context (S&P, VIX)
|
|
79
|
+
6. Check their paper_trade portfolio status
|
|
80
|
+
7. Synthesize into actionable insights with clear BUY/SELL/HOLD signals
|
|
81
|
+
|
|
82
|
+
## When the user says "analyze X":
|
|
83
|
+
1. market_data for current state
|
|
84
|
+
2. price_history for 90d trend
|
|
85
|
+
3. technical_analysis for signals
|
|
86
|
+
4. social_pulse for Reddit + news sentiment
|
|
87
|
+
5. github_activity if it's a crypto project (check dev velocity)
|
|
88
|
+
6. Give a clear BULLISH / BEARISH / NEUTRAL verdict with reasoning across all signals
|
|
89
|
+
|
|
90
|
+
## Strategy Templates
|
|
91
|
+
|
|
92
|
+
When the user asks to "set up a strategy" or "automate trading", recommend one of these:
|
|
93
|
+
|
|
94
|
+
### DCA (Dollar-Cost Averaging)
|
|
95
|
+
- Best for: long-term accumulation, beginners
|
|
96
|
+
- How: buy fixed USD amount at regular intervals regardless of price
|
|
97
|
+
- Backtest with: \`backtest_strategy symbol dca days 365\`
|
|
98
|
+
- Risk: low (time smooths volatility)
|
|
99
|
+
|
|
100
|
+
### Momentum (RSI-based)
|
|
101
|
+
- Best for: swing traders, intermediate users
|
|
102
|
+
- How: buy when RSI drops below 30 (oversold), sell when RSI rises above 70 (overbought)
|
|
103
|
+
- Backtest with: \`backtest_strategy symbol momentum days 180\`
|
|
104
|
+
- Risk: medium (can miss rallies, false signals in strong trends)
|
|
105
|
+
|
|
106
|
+
### Mean Reversion (Bollinger Bands)
|
|
107
|
+
- Best for: range-bound markets, experienced traders
|
|
108
|
+
- How: buy below lower Bollinger Band, sell above upper Band
|
|
109
|
+
- Backtest with: \`backtest_strategy symbol mean-reversion days 180\`
|
|
110
|
+
- Risk: medium-high (fails in breakout/breakdown markets)
|
|
111
|
+
|
|
112
|
+
### Portfolio Rebalance
|
|
113
|
+
- Best for: diversified holders
|
|
114
|
+
- How: set target allocation (e.g. "btc:50,eth:30,sol:20"), rebalance monthly
|
|
115
|
+
- Use: \`portfolio_rebalance targets "btc:50,eth:30,sol:20"\`
|
|
116
|
+
|
|
117
|
+
## Stock Market Tools
|
|
118
|
+
|
|
119
|
+
You also have access to traditional equities:
|
|
120
|
+
- **stock_quote** — real-time stock/ETF quotes (AAPL, SPY, etc.)
|
|
121
|
+
- **stock_history** — historical price data (1mo–max range)
|
|
122
|
+
- **stock_compare** — compare multiple tickers side by side
|
|
123
|
+
- **stock_search** — find stocks by name/keyword
|
|
124
|
+
- **stock_screener** — curated watchlists by sector (tech, ai, energy, etc.)
|
|
125
|
+
- **market_indices** — S&P 500, Nasdaq, Dow, VIX, global indices
|
|
126
|
+
|
|
127
|
+
When analyzing stocks, use the same Signal → Risk → Execution framework as crypto.
|
|
128
|
+
|
|
129
|
+
## Price Alerts
|
|
130
|
+
- **price_alert set** — set above/below thresholds
|
|
131
|
+
- **price_alert check** — check if any alerts triggered
|
|
132
|
+
- **price_alert list** — show all active alerts
|
|
133
|
+
|
|
134
|
+
## Wallet Operations
|
|
135
|
+
- **wallet_list** — show all wallets
|
|
136
|
+
- **wallet_switch** — change active wallet
|
|
137
|
+
- **wallet_send** — send SOL (with confirmation gate)
|
|
138
|
+
- **wallet_tokens** — all token balances with USD values
|
|
139
|
+
|
|
140
|
+
*Built for research and paper trading. Not a licensed financial advisor. Not financial advice.*`,
|
|
141
|
+
};
|
|
142
|
+
/** Trader agent built-in registration — matches BUILTIN_AGENTS shape in matrix.ts */
|
|
143
|
+
export const TRADER_BUILTIN = {
|
|
144
|
+
name: 'Trader',
|
|
145
|
+
icon: '📈',
|
|
146
|
+
color: '#10B981', // emerald green — money/growth
|
|
147
|
+
prompt: TRADER_PRESET.prompt,
|
|
148
|
+
};
|
|
149
|
+
/** Trader agent keyword list for learned-router.ts */
|
|
150
|
+
export const TRADER_KEYWORDS = [
|
|
151
|
+
'trade', 'trading', 'buy', 'sell', 'portfolio', 'stock', 'crypto',
|
|
152
|
+
'bitcoin', 'btc', 'ethereum', 'eth', 'solana', 'sol', 'token',
|
|
153
|
+
'price', 'market', 'chart', 'candle', 'ohlcv', 'volume',
|
|
154
|
+
'rsi', 'macd', 'bollinger', 'sma', 'ema', 'moving average',
|
|
155
|
+
'technical analysis', 'fundamental', 'sentiment', 'fear', 'greed',
|
|
156
|
+
'wallet', 'balance', 'defi', 'yield', 'apy', 'apr', 'staking',
|
|
157
|
+
'swap', 'dex', 'cex', 'exchange', 'binance', 'coinbase',
|
|
158
|
+
'long', 'short', 'position', 'stop loss', 'take profit',
|
|
159
|
+
'bull', 'bear', 'bullish', 'bearish', 'pump', 'dump', 'moon',
|
|
160
|
+
'altcoin', 'memecoin', 'nft', 'airdrop', 'rug', 'whale',
|
|
161
|
+
'paper trade', 'backtest', 'strategy', 'hedge', 'arbitrage',
|
|
162
|
+
'leverage', 'margin', 'liquidation', 'futures', 'options',
|
|
163
|
+
'market cap', 'dominance', 'tvl', 'gas', 'fees',
|
|
164
|
+
'finance', 'financial', 'investment', 'invest', 'money',
|
|
165
|
+
'reddit', 'wsb', 'wallstreetbets', 'social', 'pulse', 'news',
|
|
166
|
+
'whale', 'whale tracker', 'large transaction', 'briefing',
|
|
167
|
+
'screener', 'watchlist', 'sector', 'index', 'indices', 'vix',
|
|
168
|
+
's&p', 'nasdaq', 'dow', 'spy', 'qqq', 'etf',
|
|
169
|
+
'aapl', 'msft', 'googl', 'nvda', 'tsla', 'amzn',
|
|
170
|
+
];
|
|
171
|
+
/** Trader agent routing patterns for learned-router.ts */
|
|
172
|
+
export const TRADER_PATTERNS = [
|
|
173
|
+
/\b(buy|sell|trade|swap)\b.+\b(btc|eth|sol|crypto|token|coin)\b/i,
|
|
174
|
+
/\b(price|chart|analysis)\b.+\b(bitcoin|ethereum|solana|crypto)\b/i,
|
|
175
|
+
/\b(portfolio|position|balance|wallet)\b/i,
|
|
176
|
+
/\b(bull|bear|long|short|hedge)\b.+\b(market|crypto|trade)\b/i,
|
|
177
|
+
/\b(rsi|macd|bollinger|moving average|technical analysis)\b/i,
|
|
178
|
+
/\b(defi|yield|staking|apy|tvl)\b/i,
|
|
179
|
+
/\b(fear.*greed|market sentiment|trending coins)\b/i,
|
|
180
|
+
/\bpaper\s*trad/i,
|
|
181
|
+
/\b(how much is|what'?s the price of|check|look up)\b.+\b(btc|eth|sol|bitcoin|ethereum|solana)\b/i,
|
|
182
|
+
];
|
|
183
|
+
/** Entry point for dynamic agent loading */
|
|
184
|
+
export const agent = {
|
|
185
|
+
preset: TRADER_PRESET,
|
|
186
|
+
builtin: TRADER_BUILTIN,
|
|
187
|
+
keywords: TRADER_KEYWORDS,
|
|
188
|
+
patterns: TRADER_PATTERNS,
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=trader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trader.js","sourceRoot":"","sources":["../../src/agents/trader.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,2FAA2F;AAC3F,gFAAgF;AAChF,EAAE;AACF,oFAAoF;AACpF,sDAAsD;AAEtD,kGAAkG;AAClG,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gGAkIsF;CAC/F,CAAA;AAED,qFAAqF;AACrF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,SAAS,EAAE,+BAA+B;IACjD,MAAM,EAAE,aAAa,CAAC,MAAM;CAC7B,CAAA;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ;IACjE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO;IAC7D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ;IACvD,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB;IAC1D,oBAAoB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO;IACjE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS;IAC7D,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU;IACvD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa;IACvD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC5D,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO;IACvD,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW;IAC3D,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS;IACzD,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM;IAC/C,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO;IACvD,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;IAC5D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU;IACzD,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK;IAC5D,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAC3C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAChD,CAAA;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,iEAAiE;IACjE,mEAAmE;IACnE,0CAA0C;IAC1C,8DAA8D;IAC9D,6DAA6D;IAC7D,mCAAmC;IACnC,oDAAoD;IACpD,iBAAiB;IACjB,kGAAkG;CACnG,CAAA;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,MAAM,EAAE,aAAa;IACrB,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,eAAe;IACzB,QAAQ,EAAE,eAAe;CAC1B,CAAA"}
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { Command } from 'commander';
|
|
|
15
15
|
import { loadConfig, setupByok, setupEmbedded, isByokEnabled, isLocalProvider, disableByok, detectProvider, getByokProvider, PROVIDERS, setupOllama, setupKbotLocal, isOllamaRunning, listOllamaModels, warmOllamaModelCache } from './auth.js';
|
|
16
16
|
import { runAndPrint, runAgent, runAgentFromCheckpoint } from './agent.js';
|
|
17
17
|
import { gatherContext } from './context.js';
|
|
18
|
+
import { probeMachine } from './machine.js';
|
|
18
19
|
import { registerCoreTools, startLazyToolRegistration, ensureLazyToolsLoaded } from './tools/index.js';
|
|
19
20
|
import { clearHistory, clearMemory, compactHistory, restoreHistory } from './memory.js';
|
|
20
21
|
import { saveSession, loadSession, listSessions, deleteSession, formatSessionList, } from './sessions.js';
|
|
@@ -58,6 +59,50 @@ async function main() {
|
|
|
58
59
|
.addHelpCommand(false)
|
|
59
60
|
.action(() => { });
|
|
60
61
|
// Sub-commands
|
|
62
|
+
program
|
|
63
|
+
.command('help')
|
|
64
|
+
.description('Show help — commands, agents, support channels')
|
|
65
|
+
.action(() => {
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(` ${chalk.bold('kbot')} v${VERSION} — Open-source terminal AI agent`);
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(` ${chalk.bold('Quick Start')}`);
|
|
70
|
+
console.log(` ${chalk.dim('─'.repeat(50))}`);
|
|
71
|
+
console.log(` ${chalk.white('kbot')} Interactive REPL`);
|
|
72
|
+
console.log(` ${chalk.white('kbot "fix the bug"')} One-shot prompt`);
|
|
73
|
+
console.log(` ${chalk.white('kbot --agent researcher')} Force a specialist`);
|
|
74
|
+
console.log(` ${chalk.white('kbot --model sonnet')} Override model`);
|
|
75
|
+
console.log(` ${chalk.white('kbot local')} $0 local AI (no API key)`);
|
|
76
|
+
console.log();
|
|
77
|
+
console.log(` ${chalk.bold('Common Commands')}`);
|
|
78
|
+
console.log(` ${chalk.dim('─'.repeat(50))}`);
|
|
79
|
+
console.log(` ${chalk.white('kbot auth')} Configure your API key (20 providers)`);
|
|
80
|
+
console.log(` ${chalk.white('kbot doctor')} Diagnose setup issues`);
|
|
81
|
+
console.log(` ${chalk.white('kbot agents')} List all 25 specialist agents`);
|
|
82
|
+
console.log(` ${chalk.white('kbot status')} Full dashboard — tools, agents, stats`);
|
|
83
|
+
console.log(` ${chalk.white('kbot init')} Set up kbot for this project (60s)`);
|
|
84
|
+
console.log(` ${chalk.white('kbot update')} Update to the latest version`);
|
|
85
|
+
console.log(` ${chalk.white('kbot tutorial')} Guided walkthrough`);
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(` ${chalk.bold('Inside the REPL')}`);
|
|
88
|
+
console.log(` ${chalk.dim('─'.repeat(50))}`);
|
|
89
|
+
console.log(` ${chalk.white('/help')} Full command list`);
|
|
90
|
+
console.log(` ${chalk.white('/agent <name>')} Switch specialist`);
|
|
91
|
+
console.log(` ${chalk.white('/plan <task>')} Plan + execute complex tasks`);
|
|
92
|
+
console.log(` ${chalk.white('/save')} Save conversation`);
|
|
93
|
+
console.log(` ${chalk.white('/remember <…>')} Teach kbot something permanent`);
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(` ${chalk.bold('Need Help?')}`);
|
|
96
|
+
console.log(` ${chalk.dim('─'.repeat(50))}`);
|
|
97
|
+
console.log(` ${chalk.white('kbot doctor')} ${chalk.dim('— diagnose any setup issue')}`);
|
|
98
|
+
console.log(` ${chalk.cyan('https://discord.gg/kdMauM9abG')} ${chalk.dim('Discord community')}`);
|
|
99
|
+
console.log(` ${chalk.cyan('https://github.com/isaacsight/kernel/issues')} ${chalk.dim('Bug reports')}`);
|
|
100
|
+
console.log(` ${chalk.cyan('support@kernel.chat')} ${chalk.dim('Email (AI-assisted replies)')}`);
|
|
101
|
+
console.log();
|
|
102
|
+
console.log(` ${chalk.dim('25 specialist agents · 290+ tools · 20 providers · MIT licensed')}`);
|
|
103
|
+
console.log();
|
|
104
|
+
process.exit(0);
|
|
105
|
+
});
|
|
61
106
|
program
|
|
62
107
|
.command('version')
|
|
63
108
|
.description('Show kbot version')
|
|
@@ -419,11 +464,20 @@ async function main() {
|
|
|
419
464
|
console.log(chalk.dim(` Mode: ${config.dryRun ? 'DRY RUN (no posting)' : 'LIVE (will post)'}`));
|
|
420
465
|
console.log(chalk.dim(` Interval: ${config.pollIntervalMinutes}m`));
|
|
421
466
|
console.log();
|
|
422
|
-
// Initial cycle
|
|
467
|
+
// Initial cycle: outreach + extended (tools, agents, papers)
|
|
468
|
+
const { runExtendedDiscovery } = await import('./discovery.js');
|
|
423
469
|
await runDiscoveryCycle(config);
|
|
424
|
-
|
|
470
|
+
const extended = await runExtendedDiscovery(config);
|
|
471
|
+
if (extended.tools > 0)
|
|
472
|
+
printInfo(` Discovered ${extended.tools} new tools`);
|
|
473
|
+
if (extended.agents > 0)
|
|
474
|
+
printInfo(` Proposed ${extended.agents} new agents`);
|
|
475
|
+
if (extended.papers > 0)
|
|
476
|
+
printInfo(` Found ${extended.papers} new papers`);
|
|
477
|
+
// Poll: outreach every interval, extended every 6 hours
|
|
425
478
|
setInterval(() => runDiscoveryCycle(config), config.pollIntervalMinutes * 60 * 1000);
|
|
426
|
-
|
|
479
|
+
setInterval(() => runExtendedDiscovery(config), 6 * 60 * 60 * 1000);
|
|
480
|
+
console.log(`Polling: outreach every ${config.pollIntervalMinutes}m, tools/agents/papers every 6h. Ctrl+C to stop.`);
|
|
427
481
|
await new Promise(() => { }); // keep alive
|
|
428
482
|
});
|
|
429
483
|
discoveryCmd
|
|
@@ -500,6 +554,67 @@ async function main() {
|
|
|
500
554
|
printSuccess('Credentials updated.');
|
|
501
555
|
process.exit(0);
|
|
502
556
|
});
|
|
557
|
+
discoveryCmd
|
|
558
|
+
.command('tools')
|
|
559
|
+
.description('Show discovered tools from npm, GitHub, MCP servers')
|
|
560
|
+
.action(async () => {
|
|
561
|
+
const { getDiscoveredTools } = await import('./discovery.js');
|
|
562
|
+
const tools = getDiscoveredTools();
|
|
563
|
+
if (tools.length === 0) {
|
|
564
|
+
printInfo('No tools discovered yet. Run: kbot discovery start');
|
|
565
|
+
process.exit(0);
|
|
566
|
+
}
|
|
567
|
+
console.log();
|
|
568
|
+
printInfo(`${tools.length} tools discovered:`);
|
|
569
|
+
for (const t of tools.slice(-15)) {
|
|
570
|
+
console.log(` [${t.source}] ${t.name}`);
|
|
571
|
+
console.log(` ${t.description.slice(0, 80)}`);
|
|
572
|
+
console.log(` ${t.url}`);
|
|
573
|
+
console.log();
|
|
574
|
+
}
|
|
575
|
+
process.exit(0);
|
|
576
|
+
});
|
|
577
|
+
discoveryCmd
|
|
578
|
+
.command('agents')
|
|
579
|
+
.description('Show proposed new specialist agents')
|
|
580
|
+
.action(async () => {
|
|
581
|
+
const { getProposedAgents } = await import('./discovery.js');
|
|
582
|
+
const agents = getProposedAgents();
|
|
583
|
+
if (agents.length === 0) {
|
|
584
|
+
printInfo('No new agents proposed yet. Run: kbot discovery start');
|
|
585
|
+
process.exit(0);
|
|
586
|
+
}
|
|
587
|
+
console.log();
|
|
588
|
+
printInfo(`${agents.length} agents proposed:`);
|
|
589
|
+
for (const a of agents) {
|
|
590
|
+
console.log(` ${a.name} (${a.id})`);
|
|
591
|
+
console.log(` Why: ${a.reason}`);
|
|
592
|
+
console.log(` Prompt: ${a.systemPrompt.slice(0, 100)}...`);
|
|
593
|
+
console.log();
|
|
594
|
+
}
|
|
595
|
+
process.exit(0);
|
|
596
|
+
});
|
|
597
|
+
discoveryCmd
|
|
598
|
+
.command('papers')
|
|
599
|
+
.description('Show discovered academic papers from arXiv')
|
|
600
|
+
.action(async () => {
|
|
601
|
+
const { getDiscoveredPapers } = await import('./discovery.js');
|
|
602
|
+
const papers = getDiscoveredPapers();
|
|
603
|
+
if (papers.length === 0) {
|
|
604
|
+
printInfo('No papers discovered yet. Run: kbot discovery start');
|
|
605
|
+
process.exit(0);
|
|
606
|
+
}
|
|
607
|
+
console.log();
|
|
608
|
+
printInfo(`${papers.length} papers found:`);
|
|
609
|
+
for (const p of papers.slice(-10)) {
|
|
610
|
+
console.log(` ${p.title}`);
|
|
611
|
+
console.log(` ${p.authors}`);
|
|
612
|
+
console.log(` ${p.abstract.slice(0, 120)}...`);
|
|
613
|
+
console.log(` ${p.url}`);
|
|
614
|
+
console.log();
|
|
615
|
+
}
|
|
616
|
+
process.exit(0);
|
|
617
|
+
});
|
|
503
618
|
program
|
|
504
619
|
.command('init')
|
|
505
620
|
.description('Set up kbot for this project — detects stack, creates tools, writes config (60 seconds)')
|
|
@@ -606,30 +721,84 @@ async function main() {
|
|
|
606
721
|
process.stderr.write(formatDoctorReport(report));
|
|
607
722
|
});
|
|
608
723
|
program
|
|
724
|
+
.command('insights')
|
|
725
|
+
.description('See yourself from kbot\'s perspective — task patterns, agent usage, efficiency, knowledge')
|
|
726
|
+
.action(async () => {
|
|
727
|
+
const { generateInsights } = await import('./introspection.js');
|
|
728
|
+
process.stderr.write(generateInsights());
|
|
729
|
+
});
|
|
730
|
+
program
|
|
731
|
+
.command('reflect')
|
|
732
|
+
.description('A narrative portrait of who you are as a builder, drawn from kbot\'s learning data')
|
|
733
|
+
.action(async () => {
|
|
734
|
+
const { generateReflection } = await import('./introspection.js');
|
|
735
|
+
process.stderr.write(generateReflection());
|
|
736
|
+
});
|
|
737
|
+
program
|
|
738
|
+
.command('compare')
|
|
739
|
+
.description('Compare your patterns to the anonymous kbot collective — see what\'s unique about you')
|
|
740
|
+
.action(async () => {
|
|
741
|
+
const { generateComparison } = await import('./introspection.js');
|
|
742
|
+
process.stderr.write(generateComparison());
|
|
743
|
+
});
|
|
744
|
+
program
|
|
745
|
+
.command('machine')
|
|
746
|
+
.description('Show full system profile — hardware, GPU, OS, dev tools, AI capabilities')
|
|
747
|
+
.option('--json', 'Output as JSON')
|
|
748
|
+
.option('--refresh', 'Force fresh probe (ignore cache)')
|
|
749
|
+
.action(async (opts) => {
|
|
750
|
+
const { probeMachine, reprobeMachine, formatMachineProfile } = await import('./machine.js');
|
|
751
|
+
const profile = opts.refresh ? await reprobeMachine() : await probeMachine();
|
|
752
|
+
if (opts.json) {
|
|
753
|
+
console.log(JSON.stringify(profile, null, 2));
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
const chalk = (await import('chalk')).default;
|
|
757
|
+
const ACCENT = process.stdout.isTTY !== false ? chalk.hex('#A78BFA') : chalk;
|
|
758
|
+
process.stderr.write('\n ' + ACCENT('kbot Machine') + '\n');
|
|
759
|
+
process.stderr.write(formatMachineProfile(profile));
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
const synthCmd = program
|
|
609
763
|
.command('synthesis')
|
|
610
|
-
.description('
|
|
764
|
+
.description('Closed-loop intelligence compounding — bridge self-discovery and universe discovery');
|
|
765
|
+
synthCmd
|
|
766
|
+
.command('run')
|
|
767
|
+
.description('Run full synthesis cycle (all 8 operations)')
|
|
611
768
|
.action(async () => {
|
|
769
|
+
const { synthesize, formatSynthesisResult } = await import('./synthesis-engine.js');
|
|
770
|
+
const chalk = (await import('chalk')).default;
|
|
771
|
+
process.stderr.write('\n' + chalk.dim('Running synthesis cycle...') + '\n\n');
|
|
772
|
+
const result = synthesize();
|
|
773
|
+
console.log(formatSynthesisResult(result));
|
|
774
|
+
});
|
|
775
|
+
synthCmd
|
|
776
|
+
.command('status')
|
|
777
|
+
.description('Show synthesis engine state and stats')
|
|
778
|
+
.action(async () => {
|
|
779
|
+
const { getSynthesisEngineStats, buildSkillMap, formatSkillMap } = await import('./synthesis-engine.js');
|
|
612
780
|
const { synthesizeMemory, getInsights, getSynthesisStats } = await import('./memory-synthesis.js');
|
|
613
781
|
const { getExtendedStats } = await import('./learning.js');
|
|
614
782
|
const chalk = (await import('chalk')).default;
|
|
615
783
|
process.stderr.write('\n');
|
|
616
|
-
//
|
|
617
|
-
|
|
618
|
-
const
|
|
784
|
+
// Memory synthesis (existing)
|
|
785
|
+
synthesizeMemory();
|
|
786
|
+
const memStats = getSynthesisStats();
|
|
619
787
|
const learning = getExtendedStats();
|
|
620
788
|
const insights = getInsights(undefined, 10);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
console.log(chalk.bold('
|
|
789
|
+
// Engine stats (new)
|
|
790
|
+
const engineStats = getSynthesisEngineStats();
|
|
791
|
+
console.log(chalk.bold('═══════════════════════════════════════════════════════'));
|
|
792
|
+
console.log(chalk.bold(' KBOT SYNTHESIS ENGINE'));
|
|
793
|
+
console.log(chalk.bold('═══════════════════════════════════════════════════════'));
|
|
624
794
|
console.log('\n' + chalk.bold('## Learning Data'));
|
|
625
795
|
console.log(` Patterns: ${learning.patternsCount}`);
|
|
626
796
|
console.log(` Solutions: ${learning.solutionsCount}`);
|
|
627
797
|
console.log(` Knowledge: ${learning.knowledgeCount}`);
|
|
628
798
|
console.log(` Projects: ${learning.projectsCount}`);
|
|
629
|
-
console.log('\n' + chalk.bold('## Synthesis'));
|
|
630
|
-
console.log(`
|
|
631
|
-
console.log(`
|
|
632
|
-
console.log(` Observations: ${synthesis.observationCount ?? 0}`);
|
|
799
|
+
console.log('\n' + chalk.bold('## Memory Synthesis'));
|
|
800
|
+
console.log(` Insights: ${memStats.insightCount}`);
|
|
801
|
+
console.log(` Observations: ${memStats.observationCount}`);
|
|
633
802
|
if (insights.length > 0) {
|
|
634
803
|
console.log('\n' + chalk.bold('## Top Insights'));
|
|
635
804
|
for (const insight of insights.slice(0, 8)) {
|
|
@@ -637,6 +806,16 @@ async function main() {
|
|
|
637
806
|
console.log(` [${conf}%] ${insight.category ?? 'general'}: ${insight.text}`);
|
|
638
807
|
}
|
|
639
808
|
}
|
|
809
|
+
console.log('\n' + chalk.bold('## Closed-Loop Engine'));
|
|
810
|
+
console.log(` Cycles: ${engineStats.totalCycles}`);
|
|
811
|
+
console.log(` Last cycle: ${engineStats.lastCycleAt || 'never'}`);
|
|
812
|
+
console.log(` Tools evaluated: ${engineStats.toolsEvaluated} (${engineStats.toolsAdopted} adopted, ${engineStats.toolsRejected} rejected)`);
|
|
813
|
+
console.log(` Agents trialed: ${engineStats.agentsTrialed} (${engineStats.agentsKept} kept, ${engineStats.agentsDissolved} dissolved)`);
|
|
814
|
+
console.log(` Papers analyzed: ${engineStats.papersAnalyzed} (${engineStats.patternsImplemented} patterns)`);
|
|
815
|
+
console.log(` Corrections active: ${engineStats.correctionsActive}`);
|
|
816
|
+
console.log(` Reflections closed: ${engineStats.reflectionsClosed}`);
|
|
817
|
+
console.log(` Patterns xferred: ${engineStats.patternsTransferred}`);
|
|
818
|
+
console.log(` Engagements fed: ${engineStats.engagementsFedBack}`);
|
|
640
819
|
// Discovery data if available
|
|
641
820
|
const { existsSync, readFileSync } = await import('fs');
|
|
642
821
|
const { join } = await import('path');
|
|
@@ -652,7 +831,84 @@ async function main() {
|
|
|
652
831
|
}
|
|
653
832
|
catch { }
|
|
654
833
|
}
|
|
655
|
-
console.log('\n' + chalk.bold('
|
|
834
|
+
console.log('\n' + chalk.bold('═══════════════════════════════════════════════════════'));
|
|
835
|
+
});
|
|
836
|
+
synthCmd
|
|
837
|
+
.command('ratings')
|
|
838
|
+
.description('Display Bayesian skill map for all agents')
|
|
839
|
+
.action(async () => {
|
|
840
|
+
const { buildSkillMap, formatSkillMap } = await import('./synthesis-engine.js');
|
|
841
|
+
const map = buildSkillMap();
|
|
842
|
+
console.log('\n' + formatSkillMap(map) + '\n');
|
|
843
|
+
});
|
|
844
|
+
synthCmd
|
|
845
|
+
.command('corrections')
|
|
846
|
+
.description('Build and display active corrections for prompt injection')
|
|
847
|
+
.action(async () => {
|
|
848
|
+
const { buildActiveCorrections } = await import('./synthesis-engine.js');
|
|
849
|
+
const chalk = (await import('chalk')).default;
|
|
850
|
+
const corrections = buildActiveCorrections();
|
|
851
|
+
if (corrections.length === 0) {
|
|
852
|
+
console.log(chalk.dim('\nNo corrections extracted yet. Need more reflections or explicit corrections.\n'));
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
console.log('\n' + chalk.bold('Active Corrections (injected into prompts):'));
|
|
856
|
+
for (const c of corrections) {
|
|
857
|
+
const icon = c.severity === 'high' ? chalk.red('!!') : c.severity === 'medium' ? chalk.yellow(' !') : chalk.dim(' -');
|
|
858
|
+
console.log(` ${icon} ${c.rule}`);
|
|
859
|
+
console.log(chalk.dim(` [${c.source}, ${c.occurrences}x]`));
|
|
860
|
+
}
|
|
861
|
+
console.log('');
|
|
862
|
+
});
|
|
863
|
+
synthCmd
|
|
864
|
+
.command('tools')
|
|
865
|
+
.description('Evaluate discovered tools against failure patterns')
|
|
866
|
+
.action(async () => {
|
|
867
|
+
const { consumeDiscoveredTools } = await import('./synthesis-engine.js');
|
|
868
|
+
const { join } = await import('path');
|
|
869
|
+
const chalk = (await import('chalk')).default;
|
|
870
|
+
const discDir = join(process.cwd(), '.kbot-discovery');
|
|
871
|
+
const adoptions = consumeDiscoveredTools(discDir);
|
|
872
|
+
if (adoptions.length === 0) {
|
|
873
|
+
console.log(chalk.dim('\nNo new tools to evaluate.\n'));
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
console.log('\n' + chalk.bold(`Tool Evaluation (${adoptions.length} tools):`));
|
|
877
|
+
for (const t of adoptions) {
|
|
878
|
+
const icon = t.status === 'adopted' ? chalk.green('+') : chalk.red('×');
|
|
879
|
+
console.log(` ${icon} ${t.name} (${t.stars}★) — ${t.reason}`);
|
|
880
|
+
}
|
|
881
|
+
console.log('');
|
|
882
|
+
});
|
|
883
|
+
synthCmd
|
|
884
|
+
.command('papers')
|
|
885
|
+
.description('Extract insights from discovered academic papers')
|
|
886
|
+
.action(async () => {
|
|
887
|
+
const { extractPaperInsights } = await import('./synthesis-engine.js');
|
|
888
|
+
const { join } = await import('path');
|
|
889
|
+
const chalk = (await import('chalk')).default;
|
|
890
|
+
const discDir = join(process.cwd(), '.kbot-discovery');
|
|
891
|
+
const insights = extractPaperInsights(discDir);
|
|
892
|
+
if (insights.length === 0) {
|
|
893
|
+
console.log(chalk.dim('\nNo new papers to analyze.\n'));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
console.log('\n' + chalk.bold(`Paper Insights (${insights.length} extracted):`));
|
|
897
|
+
for (const p of insights) {
|
|
898
|
+
const icon = p.status === 'proposed' ? chalk.green('→') : chalk.dim('×');
|
|
899
|
+
console.log(` ${icon} "${p.technique}"`);
|
|
900
|
+
console.log(chalk.dim(` From: ${p.title.slice(0, 60)}...`));
|
|
901
|
+
console.log(chalk.dim(` Applies to: ${p.applicableTo}`));
|
|
902
|
+
}
|
|
903
|
+
console.log('');
|
|
904
|
+
});
|
|
905
|
+
// Default action (no subcommand) — run full cycle
|
|
906
|
+
synthCmd.action(async () => {
|
|
907
|
+
const { synthesize, formatSynthesisResult } = await import('./synthesis-engine.js');
|
|
908
|
+
const chalk = (await import('chalk')).default;
|
|
909
|
+
process.stderr.write('\n' + chalk.dim('Running synthesis cycle...') + '\n\n');
|
|
910
|
+
const result = synthesize();
|
|
911
|
+
console.log(formatSynthesisResult(result));
|
|
656
912
|
});
|
|
657
913
|
// ── Forge subcommands ──
|
|
658
914
|
const forgeCmd = program
|
|
@@ -2352,11 +2608,11 @@ async function main() {
|
|
|
2352
2608
|
}
|
|
2353
2609
|
}
|
|
2354
2610
|
}
|
|
2355
|
-
// Parallel startup: register core tools (fast), gather context, check updates, cloud sync
|
|
2611
|
+
// Parallel startup: register core tools (fast), gather context, probe machine, check updates, cloud sync
|
|
2356
2612
|
const toolOpts = { computerUse: opts.computerUse };
|
|
2357
|
-
const [,
|
|
2613
|
+
const [, machineProfile, , , syncMsg] = await Promise.all([
|
|
2358
2614
|
registerCoreTools(toolOpts),
|
|
2359
|
-
|
|
2615
|
+
probeMachine().catch(() => null),
|
|
2360
2616
|
// Non-blocking update check — fire and forget
|
|
2361
2617
|
Promise.resolve().then(() => {
|
|
2362
2618
|
try {
|
|
@@ -2368,7 +2624,10 @@ async function main() {
|
|
|
2368
2624
|
}),
|
|
2369
2625
|
// Cloud sync — pull latest learning data if available
|
|
2370
2626
|
syncOnStartup().catch(() => null),
|
|
2627
|
+
// Placeholder for alignment (5-element destructure)
|
|
2628
|
+
Promise.resolve(null),
|
|
2371
2629
|
]);
|
|
2630
|
+
const context = gatherContext(machineProfile ?? undefined);
|
|
2372
2631
|
if (syncMsg)
|
|
2373
2632
|
printInfo(syncMsg);
|
|
2374
2633
|
// Determine if we're in one-shot or REPL mode
|
|
@@ -3154,6 +3413,21 @@ async function handleSlashCommand(input, opts, rl) {
|
|
|
3154
3413
|
case 'help':
|
|
3155
3414
|
printHelp();
|
|
3156
3415
|
break;
|
|
3416
|
+
case 'insights': {
|
|
3417
|
+
const { generateInsights } = await import('./introspection.js');
|
|
3418
|
+
process.stderr.write(generateInsights());
|
|
3419
|
+
break;
|
|
3420
|
+
}
|
|
3421
|
+
case 'reflect': {
|
|
3422
|
+
const { generateReflection } = await import('./introspection.js');
|
|
3423
|
+
process.stderr.write(generateReflection());
|
|
3424
|
+
break;
|
|
3425
|
+
}
|
|
3426
|
+
case 'compare': {
|
|
3427
|
+
const { generateComparison } = await import('./introspection.js');
|
|
3428
|
+
process.stderr.write(generateComparison());
|
|
3429
|
+
break;
|
|
3430
|
+
}
|
|
3157
3431
|
case 'tutorial': {
|
|
3158
3432
|
await runTutorial(rl);
|
|
3159
3433
|
break;
|