clodds 1.1.0 → 1.2.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.
Files changed (115) hide show
  1. package/README.md +79 -47
  2. package/dist/agents/handlers/kalshi.d.ts +1 -2
  3. package/dist/agents/handlers/kalshi.js +369 -616
  4. package/dist/agents/handlers/kalshi.js.map +1 -1
  5. package/dist/agents/handlers/types.d.ts +1 -1
  6. package/dist/agents/index.d.ts +1 -1
  7. package/dist/agents/index.js +435 -710
  8. package/dist/agents/index.js.map +1 -1
  9. package/dist/bittensor/index.d.ts +3 -2
  10. package/dist/bittensor/index.js +44 -5
  11. package/dist/bittensor/index.js.map +1 -1
  12. package/dist/cli/commands/gateway.js +37 -2
  13. package/dist/cli/commands/gateway.js.map +1 -1
  14. package/dist/cli/commands/index.js +3 -2
  15. package/dist/cli/commands/index.js.map +1 -1
  16. package/dist/cli/index.js +6 -4
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cron/index.d.ts +1 -1
  19. package/dist/cron/index.js +26 -34
  20. package/dist/cron/index.js.map +1 -1
  21. package/dist/db/index.d.ts +12 -0
  22. package/dist/db/index.js +51 -23
  23. package/dist/db/index.js.map +1 -1
  24. package/dist/db/migrations.js +36 -0
  25. package/dist/db/migrations.js.map +1 -1
  26. package/dist/evm/index.d.ts +1 -0
  27. package/dist/evm/index.js +2 -0
  28. package/dist/evm/index.js.map +1 -1
  29. package/dist/evm/pancakeswap.d.ts +55 -0
  30. package/dist/evm/pancakeswap.js +299 -0
  31. package/dist/evm/pancakeswap.js.map +1 -0
  32. package/dist/exchanges/lighter/index.d.ts +95 -0
  33. package/dist/exchanges/lighter/index.js +154 -0
  34. package/dist/exchanges/lighter/index.js.map +1 -0
  35. package/dist/gateway/server.d.ts +9 -0
  36. package/dist/gateway/server.js +208 -1
  37. package/dist/gateway/server.js.map +1 -1
  38. package/dist/gateway/signal-bus.d.ts +2 -43
  39. package/dist/gateway/signal-bus.js.map +1 -1
  40. package/dist/index.js +40 -3
  41. package/dist/index.js.map +1 -1
  42. package/dist/mcp/security.d.ts +44 -0
  43. package/dist/mcp/security.js +143 -0
  44. package/dist/mcp/security.js.map +1 -0
  45. package/dist/mcp/server.js +27 -2
  46. package/dist/mcp/server.js.map +1 -1
  47. package/dist/services/alt-data/index.d.ts +1 -1
  48. package/dist/sessions/index.js +66 -7
  49. package/dist/sessions/index.js.map +1 -1
  50. package/dist/signal-router/router.d.ts +1 -1
  51. package/dist/signal-router/types.d.ts +1 -1
  52. package/dist/skills/bundled/arbitrage/index.js +96 -63
  53. package/dist/skills/bundled/arbitrage/index.js.map +1 -1
  54. package/dist/skills/bundled/betfair/index.js +121 -83
  55. package/dist/skills/bundled/betfair/index.js.map +1 -1
  56. package/dist/skills/bundled/bridge/index.js +42 -13
  57. package/dist/skills/bundled/bridge/index.js.map +1 -1
  58. package/dist/skills/bundled/crypto-hft/index.js +267 -226
  59. package/dist/skills/bundled/crypto-hft/index.js.map +1 -1
  60. package/dist/skills/bundled/drift/index.js +83 -52
  61. package/dist/skills/bundled/drift/index.js.map +1 -1
  62. package/dist/skills/bundled/embeddings/index.js +87 -49
  63. package/dist/skills/bundled/embeddings/index.js.map +1 -1
  64. package/dist/skills/bundled/execution/index.js +47 -25
  65. package/dist/skills/bundled/execution/index.js.map +1 -1
  66. package/dist/skills/bundled/feeds/index.js +45 -25
  67. package/dist/skills/bundled/feeds/index.js.map +1 -1
  68. package/dist/skills/bundled/hyperliquid/index.js +91 -45
  69. package/dist/skills/bundled/hyperliquid/index.js.map +1 -1
  70. package/dist/skills/bundled/integrations/index.js +2 -1
  71. package/dist/skills/bundled/integrations/index.js.map +1 -1
  72. package/dist/skills/bundled/kamino/index.js +71 -43
  73. package/dist/skills/bundled/kamino/index.js.map +1 -1
  74. package/dist/skills/bundled/lighter/index.d.ts +19 -0
  75. package/dist/skills/bundled/lighter/index.js +385 -0
  76. package/dist/skills/bundled/lighter/index.js.map +1 -0
  77. package/dist/skills/bundled/marginfi/index.d.ts +21 -0
  78. package/dist/skills/bundled/marginfi/index.js +387 -0
  79. package/dist/skills/bundled/marginfi/index.js.map +1 -0
  80. package/dist/skills/bundled/opinion/index.js +44 -25
  81. package/dist/skills/bundled/opinion/index.js.map +1 -1
  82. package/dist/skills/bundled/pancakeswap/index.d.ts +19 -0
  83. package/dist/skills/bundled/pancakeswap/index.js +260 -0
  84. package/dist/skills/bundled/pancakeswap/index.js.map +1 -0
  85. package/dist/skills/bundled/setup/index.d.ts +16 -0
  86. package/dist/skills/bundled/setup/index.js +427 -0
  87. package/dist/skills/bundled/setup/index.js.map +1 -0
  88. package/dist/skills/bundled/solend/index.d.ts +22 -0
  89. package/dist/skills/bundled/solend/index.js +401 -0
  90. package/dist/skills/bundled/solend/index.js.map +1 -0
  91. package/dist/skills/bundled/trading-evm/index.js +42 -11
  92. package/dist/skills/bundled/trading-evm/index.js.map +1 -1
  93. package/dist/skills/errors.d.ts +20 -0
  94. package/dist/skills/errors.js +84 -0
  95. package/dist/skills/errors.js.map +1 -0
  96. package/dist/skills/executor.d.ts +1 -1
  97. package/dist/skills/executor.js +515 -41
  98. package/dist/skills/executor.js.map +1 -1
  99. package/dist/skills/help.d.ts +33 -0
  100. package/dist/skills/help.js +73 -0
  101. package/dist/skills/help.js.map +1 -0
  102. package/dist/solana/marginfi.d.ts +112 -0
  103. package/dist/solana/marginfi.js +340 -0
  104. package/dist/solana/marginfi.js.map +1 -0
  105. package/dist/solana/solend.d.ts +120 -0
  106. package/dist/solana/solend.js +261 -0
  107. package/dist/solana/solend.js.map +1 -0
  108. package/dist/trading/bridge.d.ts +1 -1
  109. package/dist/trading/position-bridge.d.ts +1 -1
  110. package/dist/types/signal-bus.d.ts +48 -0
  111. package/dist/types/signal-bus.js +9 -0
  112. package/dist/types/signal-bus.js.map +1 -0
  113. package/dist/types.d.ts +17 -0
  114. package/dist/utils/json-utils.d.ts +8 -8
  115. package/package.json +8 -6
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Skill Executor - Central registry for all 110 bundled CLI skill handlers.
2
+ * Skill Executor - Central registry for all 118 bundled CLI skill handlers.
3
3
  *
4
4
  * ARCHITECTURE:
5
5
  * - Each skill lives in src/skills/bundled/<name>/index.ts
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Skill Executor - Central registry for all 110 bundled CLI skill handlers.
3
+ * Skill Executor - Central registry for all 118 bundled CLI skill handlers.
4
4
  *
5
5
  * ARCHITECTURE:
6
6
  * - Each skill lives in src/skills/bundled/<name>/index.ts
@@ -117,6 +117,8 @@ const SKILL_MANIFEST = [
117
117
  'jupiter',
118
118
  'kamino',
119
119
  'ledger',
120
+ 'lighter',
121
+ 'marginfi',
120
122
  'market-index',
121
123
  'markets',
122
124
  'mcp',
@@ -135,6 +137,7 @@ const SKILL_MANIFEST = [
135
137
  'opportunity',
136
138
  'orca',
137
139
  'pairing',
140
+ 'pancakeswap',
138
141
  'percolator',
139
142
  'permissions',
140
143
  'plugins',
@@ -162,6 +165,7 @@ const SKILL_MANIFEST = [
162
165
  'sizing',
163
166
  'slippage',
164
167
  'smarkets',
168
+ 'solend',
165
169
  'strategy',
166
170
  'streaming',
167
171
  'tailscale',
@@ -189,7 +193,300 @@ const SKILL_MANIFEST = [
189
193
  'token-security',
190
194
  'dca',
191
195
  'shield',
196
+ 'setup',
192
197
  ];
198
+ const SKILL_CATEGORIES = {
199
+ // DeFi & DEX
200
+ 'pancakeswap': 'DeFi & DEX',
201
+ 'bridge': 'DeFi & DEX',
202
+ 'trading-evm': 'DeFi & DEX',
203
+ 'onchainkit': 'DeFi & DEX',
204
+ 'ens': 'DeFi & DEX',
205
+ 'erc8004': 'DeFi & DEX',
206
+ 'endaoment': 'DeFi & DEX',
207
+ 'clanker': 'DeFi & DEX',
208
+ 'slippage': 'DeFi & DEX',
209
+ 'router': 'DeFi & DEX',
210
+ 'mev': 'DeFi & DEX',
211
+ 'virtuals': 'DeFi & DEX',
212
+ // Futures & Perps
213
+ 'hyperliquid': 'Futures & Perps',
214
+ 'lighter': 'Futures & Perps',
215
+ 'drift': 'Futures & Perps',
216
+ 'drift-sdk': 'Futures & Perps',
217
+ 'binance-futures': 'Futures & Perps',
218
+ 'bybit-futures': 'Futures & Perps',
219
+ 'mexc-futures': 'Futures & Perps',
220
+ 'trading-futures': 'Futures & Perps',
221
+ // Prediction Markets
222
+ 'trading-polymarket': 'Prediction Markets',
223
+ 'betfair': 'Prediction Markets',
224
+ 'trading-kalshi': 'Prediction Markets',
225
+ 'trading-manifold': 'Prediction Markets',
226
+ 'predictfun': 'Prediction Markets',
227
+ 'predictit': 'Prediction Markets',
228
+ 'smarkets': 'Prediction Markets',
229
+ 'metaculus': 'Prediction Markets',
230
+ 'veil': 'Prediction Markets',
231
+ 'agentbets': 'Prediction Markets',
232
+ 'opinion': 'Prediction Markets',
233
+ // Solana DeFi
234
+ 'jupiter': 'Solana DeFi',
235
+ 'raydium': 'Solana DeFi',
236
+ 'orca': 'Solana DeFi',
237
+ 'meteora': 'Solana DeFi',
238
+ 'meteora-dbc': 'Solana DeFi',
239
+ 'pumpfun': 'Solana DeFi',
240
+ 'pump-swarm': 'Solana DeFi',
241
+ 'kamino': 'Solana DeFi',
242
+ 'marginfi': 'Solana DeFi',
243
+ 'trading-solana': 'Solana DeFi',
244
+ 'copy-trading-solana': 'Solana DeFi',
245
+ 'solend': 'Solana DeFi',
246
+ // Portfolio & Risk
247
+ 'portfolio': 'Portfolio & Risk',
248
+ 'portfolio-sync': 'Portfolio & Risk',
249
+ 'positions': 'Portfolio & Risk',
250
+ 'bags': 'Portfolio & Risk',
251
+ 'risk': 'Portfolio & Risk',
252
+ 'sizing': 'Portfolio & Risk',
253
+ 'ledger': 'Portfolio & Risk',
254
+ 'history': 'Portfolio & Risk',
255
+ 'divergence': 'Portfolio & Risk',
256
+ 'edge': 'Portfolio & Risk',
257
+ 'dca': 'Portfolio & Risk',
258
+ // Data & Feeds
259
+ 'feeds': 'Data & Feeds',
260
+ 'markets': 'Data & Feeds',
261
+ 'market-index': 'Data & Feeds',
262
+ 'news': 'Data & Feeds',
263
+ 'ticks': 'Data & Feeds',
264
+ 'signals': 'Data & Feeds',
265
+ 'whale-tracking': 'Data & Feeds',
266
+ 'analytics': 'Data & Feeds',
267
+ 'metrics': 'Data & Feeds',
268
+ 'weather': 'Data & Feeds',
269
+ // AI & Strategy
270
+ 'ai-strategy': 'AI & Strategy',
271
+ 'strategy': 'AI & Strategy',
272
+ 'arbitrage': 'AI & Strategy',
273
+ 'copy-trading': 'AI & Strategy',
274
+ 'crypto-hft': 'AI & Strategy',
275
+ 'execution': 'AI & Strategy',
276
+ 'backtest': 'AI & Strategy',
277
+ 'pairing': 'AI & Strategy',
278
+ 'mm': 'AI & Strategy',
279
+ 'opportunity': 'AI & Strategy',
280
+ 'research': 'AI & Strategy',
281
+ 'embeddings': 'AI & Strategy',
282
+ 'features': 'AI & Strategy',
283
+ 'trading-system': 'AI & Strategy',
284
+ 'percolator': 'AI & Strategy',
285
+ // Infrastructure
286
+ 'credentials': 'Infrastructure',
287
+ 'automation': 'Infrastructure',
288
+ 'monitoring': 'Infrastructure',
289
+ 'alerts': 'Infrastructure',
290
+ 'triggers': 'Infrastructure',
291
+ 'webhooks': 'Infrastructure',
292
+ 'streaming': 'Infrastructure',
293
+ 'sessions': 'Infrastructure',
294
+ 'remote': 'Infrastructure',
295
+ 'mcp': 'Infrastructure',
296
+ 'plugins': 'Infrastructure',
297
+ 'processes': 'Infrastructure',
298
+ 'routing': 'Infrastructure',
299
+ 'tailscale': 'Infrastructure',
300
+ 'search-config': 'Infrastructure',
301
+ 'setup': 'Infrastructure',
302
+ // Social & Identity
303
+ 'farcaster': 'Social & Identity',
304
+ 'identity': 'Social & Identity',
305
+ 'presence': 'Social & Identity',
306
+ 'botchan': 'Social & Identity',
307
+ 'auto-reply': 'Social & Identity',
308
+ 'tweet-ideas': 'Social & Identity',
309
+ 'voice': 'Social & Identity',
310
+ 'tts': 'Social & Identity',
311
+ // Utilities
312
+ 'doctor': 'Utilities',
313
+ 'usage': 'Utilities',
314
+ 'verify': 'Utilities',
315
+ 'sandbox': 'Utilities',
316
+ 'permissions': 'Utilities',
317
+ 'integrations': 'Utilities',
318
+ 'memory': 'Utilities',
319
+ 'qmd': 'Utilities',
320
+ 'qrcoin': 'Utilities',
321
+ 'bankr': 'Utilities',
322
+ 'yoink': 'Utilities',
323
+ 'acp': 'Utilities',
324
+ 'harden': 'Utilities',
325
+ // Security
326
+ 'token-security': 'Security',
327
+ 'shield': 'Security',
328
+ };
329
+ // =============================================================================
330
+ // COMMAND ALIASES — alternative names that map to real commands
331
+ // =============================================================================
332
+ const COMMAND_ALIASES = {
333
+ '/pancakeswap': '/cake',
334
+ '/pancake': '/cake',
335
+ '/hyperliquid': '/hl',
336
+ '/hyper': '/hl',
337
+ '/polymarket': '/poly',
338
+ '/prediction': '/poly',
339
+ '/uniswap': '/swap',
340
+ '/sushiswap': '/swap',
341
+ '/balance': '/bags',
342
+ '/wallet': '/bags',
343
+ '/pnl': '/positions',
344
+ '/trades': '/history',
345
+ '/arb': '/arbitrage',
346
+ '/hft': '/crypto-hft',
347
+ '/bt': '/backtest',
348
+ '/creds': '/credentials',
349
+ '/keys': '/credentials',
350
+ '/mon': '/monitoring',
351
+ '/pricecheck': '/feeds',
352
+ '/sol': '/trading-solana',
353
+ '/solana': '/trading-solana',
354
+ '/evm': '/trading-evm',
355
+ '/kalshi': '/trading-kalshi',
356
+ '/manifold': '/trading-manifold',
357
+ '/security': '/token-security',
358
+ '/audit': '/token-security',
359
+ '/config': '/setup',
360
+ '/onboard': '/setup',
361
+ '/start': '/setup',
362
+ '/mrg': '/marginfi',
363
+ };
364
+ // =============================================================================
365
+ // ENV VAR DOCUMENTATION — describes what each var is for
366
+ // =============================================================================
367
+ const ENV_VAR_DOCS = {
368
+ EVM_PRIVATE_KEY: {
369
+ description: 'Private key for EVM chains (ETH, BSC, ARB, Base)',
370
+ example: 'export EVM_PRIVATE_KEY="0xabcdef..."',
371
+ },
372
+ SOLANA_PRIVATE_KEY: {
373
+ description: 'Base58 private key for Solana transactions',
374
+ example: 'export SOLANA_PRIVATE_KEY="5abc..."',
375
+ },
376
+ SOLANA_KEYPAIR_PATH: {
377
+ description: 'Path to Solana keypair JSON file (alternative to SOLANA_PRIVATE_KEY)',
378
+ example: 'export SOLANA_KEYPAIR_PATH="~/.config/solana/id.json"',
379
+ },
380
+ HYPERLIQUID_PRIVATE_KEY: {
381
+ description: 'Private key for Hyperliquid L1',
382
+ example: 'export HYPERLIQUID_PRIVATE_KEY="0xabcdef..."',
383
+ },
384
+ HYPERLIQUID_WALLET: {
385
+ description: 'Wallet address on Hyperliquid (for vault/sub-account trading)',
386
+ example: 'export HYPERLIQUID_WALLET="0x1234..."',
387
+ },
388
+ LIGHTER_API_KEY: {
389
+ description: 'API key for Lighter DEX (optional, increases rate limits)',
390
+ example: 'export LIGHTER_API_KEY="your-api-key"',
391
+ url: 'https://lighter.xyz',
392
+ },
393
+ POLY_API_KEY: {
394
+ description: 'Polymarket CLOB API key',
395
+ example: 'export POLY_API_KEY="your-key"',
396
+ url: 'https://docs.polymarket.com',
397
+ },
398
+ POLY_API_SECRET: {
399
+ description: 'Polymarket CLOB API secret',
400
+ example: 'export POLY_API_SECRET="your-secret"',
401
+ },
402
+ POLY_API_PASSPHRASE: {
403
+ description: 'Polymarket CLOB API passphrase',
404
+ example: 'export POLY_API_PASSPHRASE="your-passphrase"',
405
+ },
406
+ OPENAI_API_KEY: {
407
+ description: 'OpenAI API key for embeddings and AI features',
408
+ example: 'export OPENAI_API_KEY="sk-..."',
409
+ url: 'https://platform.openai.com/api-keys',
410
+ },
411
+ BETFAIR_APP_KEY: {
412
+ description: 'Betfair application key',
413
+ example: 'export BETFAIR_APP_KEY="your-app-key"',
414
+ url: 'https://developer.betfair.com',
415
+ },
416
+ BETFAIR_SESSION_TOKEN: {
417
+ description: 'Betfair session token for authenticated requests',
418
+ example: 'export BETFAIR_SESSION_TOKEN="your-token"',
419
+ },
420
+ BINANCE_API_KEY: {
421
+ description: 'Binance API key for futures trading',
422
+ example: 'export BINANCE_API_KEY="your-key"',
423
+ url: 'https://www.binance.com/en/my/settings/api-management',
424
+ },
425
+ BINANCE_API_SECRET: {
426
+ description: 'Binance API secret',
427
+ example: 'export BINANCE_API_SECRET="your-secret"',
428
+ },
429
+ BYBIT_API_KEY: {
430
+ description: 'Bybit API key for derivatives trading',
431
+ example: 'export BYBIT_API_KEY="your-key"',
432
+ url: 'https://www.bybit.com/app/user/api-management',
433
+ },
434
+ BYBIT_API_SECRET: {
435
+ description: 'Bybit API secret',
436
+ example: 'export BYBIT_API_SECRET="your-secret"',
437
+ },
438
+ MEXC_API_KEY: {
439
+ description: 'MEXC API key for futures trading',
440
+ example: 'export MEXC_API_KEY="your-key"',
441
+ url: 'https://www.mexc.com/user/openapi',
442
+ },
443
+ MEXC_API_SECRET: {
444
+ description: 'MEXC API secret',
445
+ example: 'export MEXC_API_SECRET="your-secret"',
446
+ },
447
+ DRY_RUN: {
448
+ description: 'When "true", simulates trades without executing',
449
+ example: 'export DRY_RUN="true"',
450
+ },
451
+ CLODDS_CREDENTIAL_KEY: {
452
+ description: 'Encryption key for credential storage (AES-256-GCM)',
453
+ example: 'export CLODDS_CREDENTIAL_KEY="your-32-char-key"',
454
+ },
455
+ };
456
+ // =============================================================================
457
+ // SKILL RELATIONS — maps skills to related skills for "See Also"
458
+ // =============================================================================
459
+ const SKILL_RELATIONS = {
460
+ 'pancakeswap': ['trading-evm', 'bridge', 'bags', 'slippage'],
461
+ 'lighter': ['hyperliquid', 'drift', 'trading-futures', 'positions'],
462
+ 'hyperliquid': ['lighter', 'drift', 'binance-futures', 'positions', 'copy-trading'],
463
+ 'drift': ['hyperliquid', 'lighter', 'trading-solana', 'positions'],
464
+ 'trading-polymarket': ['betfair', 'trading-kalshi', 'predictfun', 'feeds', 'arbitrage'],
465
+ 'betfair': ['trading-polymarket', 'smarkets', 'trading-kalshi', 'arbitrage'],
466
+ 'trading-kalshi': ['trading-polymarket', 'betfair', 'predictit'],
467
+ 'jupiter': ['raydium', 'orca', 'trading-solana', 'bags', 'kamino', 'marginfi', 'solend'],
468
+ 'marginfi': ['kamino', 'solend', 'jupiter', 'bags'],
469
+ 'raydium': ['jupiter', 'orca', 'meteora', 'pumpfun'],
470
+ 'portfolio': ['positions', 'bags', 'history', 'risk'],
471
+ 'positions': ['portfolio', 'bags', 'risk', 'history'],
472
+ 'bags': ['portfolio', 'positions', 'trading-solana', 'trading-evm'],
473
+ 'arbitrage': ['trading-polymarket', 'betfair', 'feeds', 'signals'],
474
+ 'bridge': ['trading-evm', 'pancakeswap', 'bags'],
475
+ 'credentials': ['setup', 'doctor'],
476
+ 'doctor': ['setup', 'credentials', 'monitoring'],
477
+ 'copy-trading': ['hyperliquid', 'crypto-hft', 'execution'],
478
+ 'binance-futures': ['bybit-futures', 'mexc-futures', 'hyperliquid', 'trading-futures'],
479
+ 'bybit-futures': ['binance-futures', 'mexc-futures', 'hyperliquid'],
480
+ 'mexc-futures': ['binance-futures', 'bybit-futures', 'hyperliquid'],
481
+ 'feeds': ['markets', 'signals', 'news', 'ticks'],
482
+ 'signals': ['feeds', 'strategy', 'alerts', 'triggers'],
483
+ 'risk': ['sizing', 'portfolio', 'positions'],
484
+ 'setup': ['credentials', 'doctor'],
485
+ 'token-security': ['shield', 'verify'],
486
+ 'shield': ['token-security', 'harden'],
487
+ 'solend': ['kamino', 'marginfi', 'jupiter', 'bags'],
488
+ 'kamino': ['marginfi', 'solend', 'jupiter', 'bags'],
489
+ };
193
490
  /** Normalize skill handler to consistent interface */
194
491
  function normalizeSkill(skill) {
195
492
  // Normalize commands array (some skills have {name,description,usage} format)
@@ -207,7 +504,28 @@ function normalizeSkill(skill) {
207
504
  wrappedHandle = async (args) => {
208
505
  const missing = requiredEnv.filter((v) => !process.env[v]);
209
506
  if (missing.length > 0) {
210
- return `⚠ ${skill.name} requires environment variables to be set:\n\n${missing.map((v) => ` • ${v}`).join('\n')}\n\nSet them in your environment or .env file to use this skill.`;
507
+ const lines = [`**${skill.name}** requires configuration:\n`];
508
+ for (const v of missing) {
509
+ const doc = ENV_VAR_DOCS[v];
510
+ if (doc) {
511
+ lines.push(` ${v} — ${doc.description}`);
512
+ lines.push(` ${doc.example}`);
513
+ if (doc.url)
514
+ lines.push(` Docs: ${doc.url}`);
515
+ }
516
+ else {
517
+ lines.push(` ${v}`);
518
+ }
519
+ }
520
+ lines.push('\nSet these in your environment or .env file.');
521
+ // Suggest /setup if available
522
+ const related = SKILL_RELATIONS[skill.name];
523
+ if (related) {
524
+ const suggestions = related.slice(0, 3).map(r => `/${r}`);
525
+ lines.push(`\nSee also: ${suggestions.join(', ')}`);
526
+ }
527
+ lines.push('Run /setup to configure all skills interactively.');
528
+ return lines.join('\n');
211
529
  }
212
530
  return boundHandle(args);
213
531
  };
@@ -297,53 +615,201 @@ async function initializeSkills() {
297
615
  return initializing;
298
616
  }
299
617
  /**
300
- * Register the built-in /skills command that shows skill status
618
+ * Register the built-in /skills command with categories, search, and status
301
619
  */
302
620
  function registerBuiltinSkillsCommand() {
303
621
  const skillsHandler = {
304
622
  name: 'skills-status',
305
- description: 'Show status of all loaded skills',
623
+ description: 'Browse, search, and check status of all skills',
306
624
  commands: ['/skills'],
307
- handle: async (_args) => {
308
- const lines = ['# Skill Status\n'];
309
- // Ready skills
310
- const ready = [];
311
- const needsConfig = [];
312
- for (const skill of registeredSkills) {
313
- if (skill.name === 'skills-status')
314
- continue;
315
- const reqs = skillRequirements.get(skill.name);
316
- if (reqs && reqs.length > 0) {
317
- const missing = reqs.filter((v) => !process.env[v]);
318
- if (missing.length > 0) {
319
- needsConfig.push(` ⚙ ${skill.name} — needs: ${missing.join(', ')}`);
320
- continue;
321
- }
322
- }
323
- ready.push(` ✓ ${skill.name}`);
324
- }
325
- lines.push(`## Ready (${ready.length})`);
326
- if (ready.length > 0) {
327
- lines.push(ready.join('\n'));
625
+ handle: async (args) => {
626
+ const parts = args.trim().split(/\s+/);
627
+ const subCmd = parts[0]?.toLowerCase();
628
+ // /skills search <query>
629
+ if (subCmd === 'search' || subCmd === 'find' || subCmd === 'grep') {
630
+ const query = parts.slice(1).join(' ').toLowerCase();
631
+ if (!query)
632
+ return 'Usage: /skills search <query>\nExample: /skills search swap';
633
+ return handleSkillSearch(query);
328
634
  }
329
- if (needsConfig.length > 0) {
330
- lines.push(`\n## Needs Configuration (${needsConfig.length})`);
331
- lines.push(needsConfig.join('\n'));
635
+ // /skills <category>
636
+ if (subCmd && subCmd !== 'status' && subCmd !== 'all' && subCmd !== '') {
637
+ const categories = Object.values(SKILL_CATEGORIES);
638
+ const matchedCat = categories.find(c => c.toLowerCase().includes(subCmd) || c.toLowerCase().replace(/[&\s]/g, '').includes(subCmd));
639
+ if (matchedCat)
640
+ return handleSkillCategory(matchedCat);
641
+ // Maybe they typed a skill name
642
+ const matchedSkill = registeredSkills.find(s => s.name.includes(subCmd) || s.commands.some(c => c.includes(subCmd)));
643
+ if (matchedSkill)
644
+ return handleSkillInfo(matchedSkill);
645
+ // Fuzzy search fallback
646
+ return handleSkillSearch(subCmd);
332
647
  }
333
- if (failedSkills.length > 0) {
334
- lines.push(`\n## Failed to Load (${failedSkills.length})`);
335
- for (const f of failedSkills) {
336
- // Truncate long error messages
337
- const shortErr = f.error.length > 80 ? f.error.slice(0, 77) + '...' : f.error;
338
- lines.push(` ✗ ${f.name} — ${shortErr}`);
339
- }
340
- }
341
- lines.push(`\n**Total:** ${registeredSkills.length - 1} loaded, ${failedSkills.length} failed, ${SKILL_MANIFEST.length} in manifest`);
342
- return lines.join('\n');
648
+ // /skills (default) categorized overview
649
+ return handleSkillOverview();
343
650
  },
344
651
  };
345
652
  registerSkill(skillsHandler);
346
653
  }
654
+ function handleSkillOverview() {
655
+ const lines = ['**Skills Directory**\n'];
656
+ // Group by category
657
+ const grouped = new Map();
658
+ for (const skill of registeredSkills) {
659
+ if (skill.name === 'skills-status')
660
+ continue;
661
+ const cat = SKILL_CATEGORIES[skill.name] || 'Utilities';
662
+ if (!grouped.has(cat)) {
663
+ grouped.set(cat, []);
664
+ }
665
+ const reqs = skillRequirements.get(skill.name);
666
+ const ready = !reqs || reqs.length === 0 || reqs.every((v) => !!process.env[v]);
667
+ grouped.get(cat).push({
668
+ name: skill.name,
669
+ cmds: skill.commands,
670
+ ready,
671
+ });
672
+ }
673
+ // Category display order
674
+ const categoryOrder = [
675
+ 'Futures & Perps', 'DeFi & DEX', 'Solana DeFi', 'Prediction Markets',
676
+ 'AI & Strategy', 'Portfolio & Risk', 'Data & Feeds', 'Security',
677
+ 'Infrastructure', 'Social & Identity', 'Utilities',
678
+ ];
679
+ for (const cat of categoryOrder) {
680
+ const skills = grouped.get(cat);
681
+ if (!skills || skills.length === 0)
682
+ continue;
683
+ const readyCount = skills.filter(s => s.ready).length;
684
+ lines.push(`**${cat}** (${readyCount}/${skills.length} ready)`);
685
+ for (const s of skills) {
686
+ const status = s.ready ? '+' : '-';
687
+ const cmds = s.cmds.map(c => `/${c}`).join(', ');
688
+ lines.push(` ${status} ${s.name} (${cmds})`);
689
+ }
690
+ lines.push('');
691
+ }
692
+ // Failed skills
693
+ if (failedSkills.length > 0) {
694
+ lines.push(`**Failed to Load** (${failedSkills.length})`);
695
+ for (const f of failedSkills) {
696
+ const shortErr = f.error.length > 60 ? f.error.slice(0, 57) + '...' : f.error;
697
+ lines.push(` x ${f.name} — ${shortErr}`);
698
+ }
699
+ lines.push('');
700
+ }
701
+ const total = registeredSkills.length - 1;
702
+ lines.push(`**Total:** ${total} loaded, ${failedSkills.length} failed`);
703
+ lines.push('');
704
+ lines.push('**Commands:**');
705
+ lines.push(' /skills <category> — Browse a category (e.g., /skills defi)');
706
+ lines.push(' /skills search <query> — Search skills by name or keyword');
707
+ lines.push(' /skills <name> — Info about a specific skill');
708
+ lines.push(' /setup — Configure environment for skills');
709
+ return lines.join('\n');
710
+ }
711
+ function handleSkillSearch(query) {
712
+ const results = [];
713
+ for (const skill of registeredSkills) {
714
+ if (skill.name === 'skills-status')
715
+ continue;
716
+ const searchable = `${skill.name} ${skill.description} ${skill.commands.join(' ')}`.toLowerCase();
717
+ const cat = SKILL_CATEGORIES[skill.name] || 'Utilities';
718
+ if (searchable.includes(query)) {
719
+ results.push({
720
+ name: skill.name,
721
+ cmds: skill.commands,
722
+ desc: skill.description,
723
+ cat,
724
+ });
725
+ }
726
+ }
727
+ if (results.length === 0) {
728
+ return `No skills found matching "${query}". Try /skills to browse all categories.`;
729
+ }
730
+ const lines = [`**Search results for "${query}"** (${results.length} found)\n`];
731
+ for (const r of results) {
732
+ const cmds = r.cmds.map(c => `/${c}`).join(', ');
733
+ lines.push(` ${r.name} (${cmds}) — ${r.desc}`);
734
+ lines.push(` Category: ${r.cat}`);
735
+ }
736
+ return lines.join('\n');
737
+ }
738
+ function handleSkillCategory(category) {
739
+ const skills = [];
740
+ for (const skill of registeredSkills) {
741
+ if (skill.name === 'skills-status')
742
+ continue;
743
+ const cat = SKILL_CATEGORIES[skill.name];
744
+ if (cat !== category)
745
+ continue;
746
+ const reqs = skillRequirements.get(skill.name);
747
+ const ready = !reqs || reqs.length === 0 || reqs.every((v) => !!process.env[v]);
748
+ skills.push({ name: skill.name, cmds: skill.commands, desc: skill.description, ready });
749
+ }
750
+ if (skills.length === 0) {
751
+ return `No skills in category "${category}".`;
752
+ }
753
+ const lines = [`**${category}** (${skills.length} skills)\n`];
754
+ for (const s of skills) {
755
+ const status = s.ready ? '+' : '-';
756
+ const cmds = s.cmds.map(c => `/${c}`).join(', ');
757
+ lines.push(` ${status} **${s.name}** (${cmds})`);
758
+ lines.push(` ${s.desc}`);
759
+ const reqs = skillRequirements.get(s.name);
760
+ if (reqs && reqs.length > 0) {
761
+ const missing = reqs.filter(v => !process.env[v]);
762
+ if (missing.length > 0) {
763
+ lines.push(` Needs: ${missing.join(', ')}`);
764
+ }
765
+ }
766
+ const related = SKILL_RELATIONS[s.name];
767
+ if (related) {
768
+ lines.push(` See also: ${related.slice(0, 3).map(r => `/${r}`).join(', ')}`);
769
+ }
770
+ }
771
+ return lines.join('\n');
772
+ }
773
+ function handleSkillInfo(skill) {
774
+ const lines = [`**${skill.name}**`];
775
+ lines.push(skill.description);
776
+ lines.push('');
777
+ const cat = SKILL_CATEGORIES[skill.name] || 'Utilities';
778
+ lines.push(`Category: ${cat}`);
779
+ lines.push(`Commands: ${skill.commands.map(c => `/${c}`).join(', ')}`);
780
+ const reqs = skillRequirements.get(skill.name);
781
+ if (reqs && reqs.length > 0) {
782
+ const missing = reqs.filter(v => !process.env[v]);
783
+ lines.push('');
784
+ lines.push('**Environment Variables:**');
785
+ for (const v of reqs) {
786
+ const doc = ENV_VAR_DOCS[v];
787
+ const status = process.env[v] ? 'set' : 'MISSING';
788
+ if (doc) {
789
+ lines.push(` ${status === 'set' ? '+' : '-'} ${v} (${status}) — ${doc.description}`);
790
+ if (status !== 'set')
791
+ lines.push(` ${doc.example}`);
792
+ }
793
+ else {
794
+ lines.push(` ${status === 'set' ? '+' : '-'} ${v} (${status})`);
795
+ }
796
+ }
797
+ }
798
+ const related = SKILL_RELATIONS[skill.name];
799
+ if (related && related.length > 0) {
800
+ lines.push('');
801
+ lines.push('**Related Skills:**');
802
+ for (const r of related) {
803
+ const relSkill = registeredSkills.find(s => s.name === r);
804
+ if (relSkill) {
805
+ lines.push(` /${relSkill.commands[0] || r} — ${relSkill.description}`);
806
+ }
807
+ }
808
+ }
809
+ lines.push('');
810
+ lines.push(`Run /${skill.commands[0] || skill.name} help for full command list.`);
811
+ return lines.join('\n');
812
+ }
347
813
  /** Map of /command → dispatch target for skills with command-dispatch: tool */
348
814
  const dispatchMap = new Map();
349
815
  /**
@@ -384,8 +850,12 @@ async function executeSkillCommand(message) {
384
850
  }
385
851
  // Parse command and arguments
386
852
  const spaceIndex = trimmed.indexOf(' ');
387
- const command = spaceIndex === -1 ? trimmed.toLowerCase() : trimmed.slice(0, spaceIndex).toLowerCase();
853
+ let command = spaceIndex === -1 ? trimmed.toLowerCase() : trimmed.slice(0, spaceIndex).toLowerCase();
388
854
  const args = spaceIndex === -1 ? '' : trimmed.slice(spaceIndex + 1);
855
+ // Resolve aliases (e.g., /pancakeswap → /cake, /start → /setup)
856
+ if (COMMAND_ALIASES[command]) {
857
+ command = COMMAND_ALIASES[command];
858
+ }
389
859
  // Check dispatch map first (command-dispatch: tool skills bypass LLM)
390
860
  const dispatch = dispatchMap.get(command);
391
861
  if (dispatch) {
@@ -434,14 +904,18 @@ function getRegisteredSkills() {
434
904
  * Get skill handler by command
435
905
  */
436
906
  function getSkillByCommand(command) {
437
- const normalized = command.toLowerCase().startsWith('/') ? command.toLowerCase() : `/${command.toLowerCase()}`;
907
+ let normalized = command.toLowerCase().startsWith('/') ? command.toLowerCase() : `/${command.toLowerCase()}`;
908
+ if (COMMAND_ALIASES[normalized])
909
+ normalized = COMMAND_ALIASES[normalized];
438
910
  return commandToSkill.get(normalized);
439
911
  }
440
912
  /**
441
913
  * Check if a command is handled by a skill (handler or dispatch)
442
914
  */
443
915
  function isSkillCommand(command) {
444
- const normalized = command.toLowerCase().startsWith('/') ? command.toLowerCase() : `/${command.toLowerCase()}`;
916
+ let normalized = command.toLowerCase().startsWith('/') ? command.toLowerCase() : `/${command.toLowerCase()}`;
917
+ if (COMMAND_ALIASES[normalized])
918
+ normalized = COMMAND_ALIASES[normalized];
445
919
  return commandToSkill.has(normalized) || dispatchMap.has(normalized);
446
920
  }
447
921
  /**