pnpfucius 2.0.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.
@@ -0,0 +1,361 @@
1
+ // Slash command handlers for PNPFUCIUS
2
+
3
+ import chalk from 'chalk';
4
+ import { printHelp, printGoodbye, printWelcome, printDivider } from './ui/welcome.js';
5
+ import { executeTool, getToolsByCategory } from './tools/index.js';
6
+ import { getConfig } from '../config.js';
7
+
8
+ // Purple color definitions
9
+ const purple = chalk.hex('#A855F7');
10
+ const purpleBright = chalk.hex('#C084FC');
11
+ const purpleDim = chalk.hex('#7C3AED');
12
+ const violet = chalk.hex('#8B5CF6');
13
+
14
+ /**
15
+ * Handle slash commands
16
+ * @param {string} input - User input
17
+ * @param {object} context - Command context (agent instance, etc.)
18
+ * @returns {boolean} True if handled, false if not a command
19
+ */
20
+ export async function handleSlashCommand(input, context = {}) {
21
+ const trimmed = input.trim();
22
+
23
+ if (!trimmed.startsWith('/')) {
24
+ return false;
25
+ }
26
+
27
+ const [command, ...args] = trimmed.slice(1).split(' ');
28
+ const argString = args.join(' ');
29
+ const { agent, executeTool: execTool } = context;
30
+
31
+ switch (command.toLowerCase()) {
32
+ case 'help':
33
+ case 'h':
34
+ case '?':
35
+ printHelp();
36
+ return true;
37
+
38
+ case 'exit':
39
+ case 'quit':
40
+ case 'q':
41
+ printGoodbye();
42
+ process.exit(0);
43
+
44
+ case 'clear':
45
+ case 'cls':
46
+ printWelcome();
47
+ return true;
48
+
49
+ case 'config':
50
+ await showConfig();
51
+ return true;
52
+
53
+ // ========== MARKET COMMANDS ==========
54
+
55
+ case 'discover':
56
+ case 'explore':
57
+ case 'markets':
58
+ const version = args[0] || 'all';
59
+ if (execTool) {
60
+ await execTool('discover_all_markets', { version });
61
+ }
62
+ return true;
63
+
64
+ case 'create':
65
+ if (argString) {
66
+ if (execTool) {
67
+ await execTool('create_market', {
68
+ question: argString,
69
+ liquidity_usdc: 1,
70
+ duration_days: 30
71
+ });
72
+ }
73
+ } else {
74
+ console.log(purpleDim('\n Usage: /create <market question>'));
75
+ console.log(chalk.dim(' Example: /create Will BTC reach $100k by 2025?'));
76
+ }
77
+ return true;
78
+
79
+ case 'p2p':
80
+ if (argString) {
81
+ if (execTool) {
82
+ await execTool('create_p2p_market_simple', {
83
+ question: argString,
84
+ side: 'yes',
85
+ amount_usdc: 1
86
+ });
87
+ }
88
+ } else {
89
+ console.log(purpleDim('\n Usage: /p2p <market question>'));
90
+ console.log(chalk.dim(' Example: /p2p Will ETH flip BTC in 2025?'));
91
+ }
92
+ return true;
93
+
94
+ case 'odds':
95
+ if (args.length >= 2) {
96
+ const oddsPercent = parseInt(args[0]);
97
+ const question = args.slice(1).join(' ');
98
+ if (execTool && !isNaN(oddsPercent)) {
99
+ await execTool('create_amm_market_with_odds', {
100
+ question,
101
+ yes_odds_percent: oddsPercent,
102
+ liquidity_usdc: 1
103
+ });
104
+ }
105
+ } else {
106
+ console.log(purpleDim('\n Usage: /odds <percent> <question>'));
107
+ console.log(chalk.dim(' Example: /odds 70 Will SOL reach $500?'));
108
+ }
109
+ return true;
110
+
111
+ case 'info':
112
+ if (argString) {
113
+ if (execTool) {
114
+ await execTool('get_market_info', { market_address: argString });
115
+ }
116
+ } else {
117
+ console.log(purpleDim('\n Usage: /info <market_address>'));
118
+ }
119
+ return true;
120
+
121
+ // ========== TRADING COMMANDS ==========
122
+
123
+ case 'buy':
124
+ if (args.length >= 3) {
125
+ if (execTool) {
126
+ await execTool('buy_tokens', {
127
+ market_address: args[0],
128
+ side: args[1],
129
+ amount_usdc: parseFloat(args[2])
130
+ });
131
+ }
132
+ } else {
133
+ console.log(purpleDim('\n Usage: /buy <market_address> <yes|no> <usdc_amount>'));
134
+ console.log(chalk.dim(' Example: /buy 7xKXw9... yes 10'));
135
+ }
136
+ return true;
137
+
138
+ case 'sell':
139
+ if (args.length >= 3) {
140
+ if (execTool) {
141
+ await execTool('sell_tokens', {
142
+ market_address: args[0],
143
+ side: args[1],
144
+ amount: parseFloat(args[2])
145
+ });
146
+ }
147
+ } else {
148
+ console.log(purpleDim('\n Usage: /sell <market_address> <yes|no> <token_amount>'));
149
+ }
150
+ return true;
151
+
152
+ case 'prices':
153
+ case 'price':
154
+ if (argString) {
155
+ if (execTool) {
156
+ await execTool('get_market_prices', { market_address: argString });
157
+ }
158
+ } else {
159
+ console.log(purpleDim('\n Usage: /prices <market_address>'));
160
+ }
161
+ return true;
162
+
163
+ case 'balance':
164
+ case 'balances':
165
+ if (argString) {
166
+ if (execTool) {
167
+ await execTool('get_balances', { market_address: argString });
168
+ }
169
+ } else {
170
+ console.log(purpleDim('\n Usage: /balance <market_address>'));
171
+ }
172
+ return true;
173
+
174
+ // ========== SETTLEMENT COMMANDS ==========
175
+
176
+ case 'oracle':
177
+ case 'criteria':
178
+ if (argString) {
179
+ if (execTool) {
180
+ await execTool('get_settlement_criteria', { market_address: argString });
181
+ }
182
+ } else {
183
+ console.log(purpleDim('\n Usage: /oracle <market_address>'));
184
+ console.log(chalk.dim(' Get AI-generated settlement criteria from PNP Oracle'));
185
+ }
186
+ return true;
187
+
188
+ case 'settlement':
189
+ case 'resolve':
190
+ if (argString) {
191
+ if (execTool) {
192
+ await execTool('get_settlement_data', { market_address: argString });
193
+ }
194
+ } else {
195
+ console.log(purpleDim('\n Usage: /settlement <market_address>'));
196
+ console.log(chalk.dim(' Get resolution result from PNP Oracle'));
197
+ }
198
+ return true;
199
+
200
+ case 'settle':
201
+ if (args.length >= 2) {
202
+ if (execTool) {
203
+ await execTool('settle_market', {
204
+ market_address: args[0],
205
+ outcome: args[1]
206
+ });
207
+ }
208
+ } else {
209
+ console.log(purpleDim('\n Usage: /settle <market_address> <yes|no>'));
210
+ console.log(chalk.dim(' Settle market with specified outcome (requires authority)'));
211
+ }
212
+ return true;
213
+
214
+ case 'redeem':
215
+ if (argString) {
216
+ if (execTool) {
217
+ await execTool('redeem_position', { market_address: argString });
218
+ }
219
+ } else {
220
+ console.log(purpleDim('\n Usage: /redeem <market_address>'));
221
+ }
222
+ return true;
223
+
224
+ case 'refund':
225
+ if (argString) {
226
+ if (execTool) {
227
+ await execTool('claim_refund', { market_address: argString });
228
+ }
229
+ } else {
230
+ console.log(purpleDim('\n Usage: /refund <market_address>'));
231
+ }
232
+ return true;
233
+
234
+ // ========== INFO COMMANDS ==========
235
+
236
+ case 'pnp':
237
+ if (execTool) {
238
+ await execTool('get_pnp_config', {});
239
+ }
240
+ return true;
241
+
242
+ case 'tools':
243
+ await showTools();
244
+ return true;
245
+
246
+ default:
247
+ console.log(chalk.yellow(`\n Unknown command: /${command}`));
248
+ console.log(chalk.dim(' Type /help for available commands.'));
249
+ return true;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Show current configuration
255
+ */
256
+ async function showConfig() {
257
+ const config = getConfig();
258
+
259
+ console.log();
260
+ console.log(purpleBright(' Configuration'));
261
+ console.log(purple(' ' + '─'.repeat(50)));
262
+ console.log();
263
+
264
+ const items = [
265
+ ['Network', config.isMainnet ? chalk.red('mainnet') : chalk.green('devnet')],
266
+ ['RPC URL', chalk.dim(config.rpcUrl.slice(0, 40) + '...')],
267
+ ['Helius API', config.heliusKey ? chalk.green('Configured') : chalk.yellow('Not set')],
268
+ ['Wallet', config.walletKey ? chalk.green('Configured') : chalk.red('Not set')],
269
+ ['Collateral', config.collateralToken],
270
+ ['Default Liquidity', config.defaultLiquidity.toString()],
271
+ ['Default Duration', `${config.defaultDurationDays} days`]
272
+ ];
273
+
274
+ for (const [key, value] of items) {
275
+ console.log(` ${chalk.dim(key.padEnd(18))} ${value}`);
276
+ }
277
+
278
+ console.log();
279
+ }
280
+
281
+ /**
282
+ * Show available tools
283
+ */
284
+ async function showTools() {
285
+ const categories = getToolsByCategory();
286
+
287
+ console.log();
288
+ console.log(purpleBright(' Available Tools'));
289
+ console.log(purple(' ' + '─'.repeat(50)));
290
+ console.log();
291
+
292
+ const toolCategories = {
293
+ 'Market Creation': [
294
+ 'create_market', 'create_p2p_market_simple',
295
+ 'create_amm_market_with_odds', 'create_p2p_market_with_odds',
296
+ 'create_market_with_oracle', 'create_market_from_source'
297
+ ],
298
+ 'Market Discovery': [
299
+ 'discover_all_markets', 'list_markets', 'get_market_info',
300
+ 'get_v2_market_info', 'get_p2p_market_info', 'get_market_metadata'
301
+ ],
302
+ 'Trading': [
303
+ 'buy_tokens', 'sell_tokens', 'buy_v3_tokens',
304
+ 'get_market_prices', 'get_balances'
305
+ ],
306
+ 'Settlement (PNP Oracle)': [
307
+ 'get_settlement_criteria', 'get_settlement_data',
308
+ 'wait_for_settlement', 'settle_market'
309
+ ],
310
+ 'Redemption': [
311
+ 'redeem_position', 'redeem_v3_position', 'redeem_p2p_position',
312
+ 'claim_refund', 'claim_p2p_refund'
313
+ ],
314
+ 'Protocol': [
315
+ 'get_pnp_config'
316
+ ]
317
+ };
318
+
319
+ for (const [category, tools] of Object.entries(toolCategories)) {
320
+ console.log(violet(` ${category}`));
321
+ for (const tool of tools) {
322
+ console.log(` ${purple('◆')} ${tool}`);
323
+ }
324
+ console.log();
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Get list of available commands for help text
330
+ */
331
+ export function getCommandList() {
332
+ return [
333
+ // Markets
334
+ { command: '/discover', description: 'Browse all PNP markets' },
335
+ { command: '/create <q>', description: 'Create AMM market' },
336
+ { command: '/p2p <q>', description: 'Create P2P market' },
337
+ { command: '/odds <% q>', description: 'Market with custom odds' },
338
+ { command: '/info <addr>', description: 'Market details' },
339
+
340
+ // Trading
341
+ { command: '/buy', description: 'Buy YES/NO tokens' },
342
+ { command: '/sell', description: 'Sell tokens' },
343
+ { command: '/prices <addr>', description: 'Get market prices' },
344
+ { command: '/balance <addr>', description: 'Check balances' },
345
+
346
+ // Settlement
347
+ { command: '/oracle <addr>', description: 'Get settlement criteria' },
348
+ { command: '/settlement <addr>', description: 'Get resolution data' },
349
+ { command: '/settle <a> <y/n>', description: 'Settle market' },
350
+ { command: '/redeem <addr>', description: 'Redeem position' },
351
+ { command: '/refund <addr>', description: 'Claim refund' },
352
+
353
+ // System
354
+ { command: '/pnp', description: 'PNP protocol info' },
355
+ { command: '/config', description: 'Show configuration' },
356
+ { command: '/tools', description: 'List all tools' },
357
+ { command: '/clear', description: 'Clear screen' },
358
+ { command: '/help', description: 'Show help' },
359
+ { command: '/exit', description: 'Exit PNPFUCIUS' }
360
+ ];
361
+ }
@@ -0,0 +1,83 @@
1
+ // Analytics tool definitions and implementations for Claude Predict
2
+
3
+ import { createAggregator } from '../../analytics/aggregator.js';
4
+ import { PRIVACY_CATEGORIES, listCategories } from '../../privacy-markets.js';
5
+
6
+ // Tool definitions for Claude API
7
+ export const analyticsToolDefinitions = [
8
+ {
9
+ name: 'get_stats',
10
+ description: 'Get market statistics and analytics',
11
+ input_schema: {
12
+ type: 'object',
13
+ properties: {
14
+ period: {
15
+ type: 'string',
16
+ enum: ['24h', '7d', '30d', 'all'],
17
+ description: 'Time period for statistics (default: all)'
18
+ }
19
+ }
20
+ }
21
+ },
22
+ {
23
+ name: 'get_categories',
24
+ description: 'List available market categories with their templates and weights',
25
+ input_schema: {
26
+ type: 'object',
27
+ properties: {}
28
+ }
29
+ }
30
+ ];
31
+
32
+ // Tool implementations
33
+ export async function executeAnalyticsTool(name, input) {
34
+ switch (name) {
35
+ case 'get_stats': {
36
+ const aggregator = createAggregator();
37
+ const overview = await aggregator.getOverview();
38
+ const period = input.period || 'all';
39
+
40
+ // Calculate period-specific stats
41
+ let periodStats = overview;
42
+ if (period !== 'all') {
43
+ const periodMs = {
44
+ '24h': 24 * 60 * 60 * 1000,
45
+ '7d': 7 * 24 * 60 * 60 * 1000,
46
+ '30d': 30 * 24 * 60 * 60 * 1000
47
+ }[period];
48
+
49
+ const cutoff = Date.now() - periodMs;
50
+ // Filter stats by period if aggregator supports it
51
+ periodStats = {
52
+ ...overview,
53
+ period,
54
+ period_start: new Date(cutoff).toISOString()
55
+ };
56
+ }
57
+
58
+ return {
59
+ period,
60
+ ...periodStats
61
+ };
62
+ }
63
+
64
+ case 'get_categories': {
65
+ const categories = listCategories();
66
+ return {
67
+ categories: categories.map(cat => ({
68
+ id: cat.id,
69
+ name: cat.name,
70
+ description: cat.description,
71
+ weight: cat.weight,
72
+ urgency: cat.urgency,
73
+ template_count: cat.templates?.length || 0,
74
+ examples: cat.templates?.slice(0, 2) || []
75
+ })),
76
+ total: categories.length
77
+ };
78
+ }
79
+
80
+ default:
81
+ return { error: `Unknown analytics tool: ${name}` };
82
+ }
83
+ }
@@ -0,0 +1,87 @@
1
+ // Bash tool definition and implementation for Claude Predict
2
+
3
+ import { spawn } from 'child_process';
4
+
5
+ // Tool definition for Claude API
6
+ export const bashToolDefinition = {
7
+ name: 'run_command',
8
+ description: 'Execute a shell command and return output. Use for npm, git, and other CLI operations.',
9
+ input_schema: {
10
+ type: 'object',
11
+ properties: {
12
+ command: {
13
+ type: 'string',
14
+ description: 'The shell command to execute'
15
+ },
16
+ cwd: {
17
+ type: 'string',
18
+ description: 'Working directory for the command (default: current directory)'
19
+ },
20
+ timeout: {
21
+ type: 'number',
22
+ description: 'Timeout in milliseconds (default: 30000)'
23
+ }
24
+ },
25
+ required: ['command']
26
+ }
27
+ };
28
+
29
+ // Tool implementation
30
+ export async function executeBashTool(name, input) {
31
+ if (name !== 'run_command') {
32
+ return { error: `Unknown bash tool: ${name}` };
33
+ }
34
+
35
+ const { command, cwd, timeout = 30000 } = input;
36
+
37
+ return new Promise((resolve) => {
38
+ const isWindows = process.platform === 'win32';
39
+ const shell = isWindows ? 'cmd.exe' : '/bin/sh';
40
+ const shellFlag = isWindows ? '/c' : '-c';
41
+
42
+ const proc = spawn(shell, [shellFlag, command], {
43
+ cwd: cwd || process.cwd(),
44
+ timeout,
45
+ env: { ...process.env }
46
+ });
47
+
48
+ let stdout = '';
49
+ let stderr = '';
50
+
51
+ proc.stdout.on('data', (data) => {
52
+ stdout += data.toString();
53
+ });
54
+
55
+ proc.stderr.on('data', (data) => {
56
+ stderr += data.toString();
57
+ });
58
+
59
+ proc.on('close', (code) => {
60
+ resolve({
61
+ command,
62
+ exit_code: code,
63
+ stdout: stdout.trim(),
64
+ stderr: stderr.trim(),
65
+ success: code === 0
66
+ });
67
+ });
68
+
69
+ proc.on('error', (err) => {
70
+ resolve({
71
+ command,
72
+ error: err.message,
73
+ success: false
74
+ });
75
+ });
76
+
77
+ // Handle timeout
78
+ setTimeout(() => {
79
+ proc.kill();
80
+ resolve({
81
+ command,
82
+ error: 'Command timed out',
83
+ success: false
84
+ });
85
+ }, timeout);
86
+ });
87
+ }
@@ -0,0 +1,140 @@
1
+ // File tool definitions and implementations for Claude Predict
2
+
3
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
4
+ import { resolve } from 'path';
5
+
6
+ // Tool definitions for Claude API
7
+ export const fileToolDefinitions = [
8
+ {
9
+ name: 'read_file',
10
+ description: 'Read contents of a file',
11
+ input_schema: {
12
+ type: 'object',
13
+ properties: {
14
+ path: {
15
+ type: 'string',
16
+ description: 'Path to the file to read'
17
+ }
18
+ },
19
+ required: ['path']
20
+ }
21
+ },
22
+ {
23
+ name: 'write_file',
24
+ description: 'Write content to a file',
25
+ input_schema: {
26
+ type: 'object',
27
+ properties: {
28
+ path: {
29
+ type: 'string',
30
+ description: 'Path to the file to write'
31
+ },
32
+ content: {
33
+ type: 'string',
34
+ description: 'Content to write to the file'
35
+ }
36
+ },
37
+ required: ['path', 'content']
38
+ }
39
+ },
40
+ {
41
+ name: 'list_files',
42
+ description: 'List files in a directory',
43
+ input_schema: {
44
+ type: 'object',
45
+ properties: {
46
+ path: {
47
+ type: 'string',
48
+ description: 'Directory path (default: current directory)'
49
+ },
50
+ pattern: {
51
+ type: 'string',
52
+ description: 'Optional glob pattern to filter files'
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ];
58
+
59
+ // Tool implementations
60
+ export async function executeFileTool(name, input) {
61
+ switch (name) {
62
+ case 'read_file': {
63
+ const filePath = resolve(input.path);
64
+
65
+ if (!existsSync(filePath)) {
66
+ return { error: `File not found: ${input.path}` };
67
+ }
68
+
69
+ try {
70
+ const content = readFileSync(filePath, 'utf-8');
71
+ return {
72
+ path: input.path,
73
+ content,
74
+ size: content.length,
75
+ lines: content.split('\n').length
76
+ };
77
+ } catch (err) {
78
+ return { error: `Failed to read file: ${err.message}` };
79
+ }
80
+ }
81
+
82
+ case 'write_file': {
83
+ const filePath = resolve(input.path);
84
+
85
+ try {
86
+ writeFileSync(filePath, input.content, 'utf-8');
87
+ return {
88
+ success: true,
89
+ path: input.path,
90
+ size: input.content.length,
91
+ lines: input.content.split('\n').length
92
+ };
93
+ } catch (err) {
94
+ return { error: `Failed to write file: ${err.message}` };
95
+ }
96
+ }
97
+
98
+ case 'list_files': {
99
+ const { readdirSync, statSync } = await import('fs');
100
+ const dirPath = resolve(input.path || '.');
101
+
102
+ if (!existsSync(dirPath)) {
103
+ return { error: `Directory not found: ${input.path}` };
104
+ }
105
+
106
+ try {
107
+ const entries = readdirSync(dirPath);
108
+ const files = entries.map(entry => {
109
+ const fullPath = resolve(dirPath, entry);
110
+ const stat = statSync(fullPath);
111
+ return {
112
+ name: entry,
113
+ type: stat.isDirectory() ? 'directory' : 'file',
114
+ size: stat.isFile() ? stat.size : null
115
+ };
116
+ });
117
+
118
+ // Filter by pattern if provided
119
+ let filtered = files;
120
+ if (input.pattern) {
121
+ const regex = new RegExp(
122
+ input.pattern.replace(/\*/g, '.*').replace(/\?/g, '.')
123
+ );
124
+ filtered = files.filter(f => regex.test(f.name));
125
+ }
126
+
127
+ return {
128
+ path: input.path || '.',
129
+ files: filtered,
130
+ total: filtered.length
131
+ };
132
+ } catch (err) {
133
+ return { error: `Failed to list directory: ${err.message}` };
134
+ }
135
+ }
136
+
137
+ default:
138
+ return { error: `Unknown file tool: ${name}` };
139
+ }
140
+ }