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.
- package/README.md +396 -0
- package/bin/claude-predict.js +5 -0
- package/bin/pnpfucius.js +8 -0
- package/package.json +71 -0
- package/src/agent.js +1037 -0
- package/src/ai/index.js +6 -0
- package/src/ai/market-generator.js +186 -0
- package/src/ai/resolver.js +172 -0
- package/src/ai/scorer.js +184 -0
- package/src/analytics/aggregator.js +198 -0
- package/src/cli.js +948 -0
- package/src/collateral/privacy-tokens.js +183 -0
- package/src/config.js +128 -0
- package/src/daemon/index.js +321 -0
- package/src/daemon/lifecycle.js +168 -0
- package/src/daemon/scheduler.js +252 -0
- package/src/events/emitter.js +147 -0
- package/src/helius/client.js +221 -0
- package/src/helius/transaction-tracker.js +192 -0
- package/src/helius/webhooks.js +233 -0
- package/src/index.js +139 -0
- package/src/monitoring/news-monitor.js +262 -0
- package/src/monitoring/news-scorer.js +236 -0
- package/src/predict/agent.js +291 -0
- package/src/predict/prompts.js +69 -0
- package/src/predict/slash-commands.js +361 -0
- package/src/predict/tools/analytics-tools.js +83 -0
- package/src/predict/tools/bash-tool.js +87 -0
- package/src/predict/tools/file-tools.js +140 -0
- package/src/predict/tools/index.js +120 -0
- package/src/predict/tools/market-tools.js +851 -0
- package/src/predict/tools/news-tools.js +130 -0
- package/src/predict/ui/renderer.js +215 -0
- package/src/predict/ui/welcome.js +146 -0
- package/src/privacy-markets.js +194 -0
- package/src/storage/market-store.js +418 -0
- package/src/utils/spinner.js +172 -0
|
@@ -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
|
+
}
|