prpm 0.1.5 → 0.1.7

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,224 @@
1
+ "use strict";
2
+ /**
3
+ * Buy Credits command - Purchase one-time playground credits
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleBuyCredits = handleBuyCredits;
7
+ exports.createBuyCreditsCommand = createBuyCreditsCommand;
8
+ const commander_1 = require("commander");
9
+ const user_config_1 = require("../core/user-config");
10
+ const telemetry_1 = require("../core/telemetry");
11
+ const child_process_1 = require("child_process");
12
+ const util_1 = require("util");
13
+ const webapp_url_1 = require("../utils/webapp-url");
14
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
15
+ /**
16
+ * Make authenticated API call
17
+ */
18
+ async function apiCall(endpoint) {
19
+ const config = await (0, user_config_1.getConfig)();
20
+ const baseUrl = (config.registryUrl || 'https://registry.prpm.dev').replace(/\/$/, '');
21
+ if (!config.token) {
22
+ throw new Error('Authentication required. Please run `prpm login` first.');
23
+ }
24
+ const response = await fetch(`${baseUrl}${endpoint}`, {
25
+ headers: {
26
+ Authorization: `Bearer ${config.token}`,
27
+ },
28
+ });
29
+ if (!response.ok) {
30
+ const errorData = await response.json().catch(() => ({}));
31
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
32
+ }
33
+ return response;
34
+ }
35
+ /**
36
+ * Get current credits balance
37
+ */
38
+ async function getBalance() {
39
+ const response = await apiCall('/api/v1/playground/credits');
40
+ return response.json();
41
+ }
42
+ /**
43
+ * Open URL in default browser
44
+ */
45
+ async function openBrowser(url) {
46
+ const platform = process.platform;
47
+ let command;
48
+ if (platform === 'darwin') {
49
+ command = `open "${url}"`;
50
+ }
51
+ else if (platform === 'win32') {
52
+ command = `start "" "${url}"`;
53
+ }
54
+ else {
55
+ // Linux and other Unix-like systems
56
+ command = `xdg-open "${url}"`;
57
+ }
58
+ try {
59
+ await execAsync(command);
60
+ }
61
+ catch (error) {
62
+ // If automatic opening fails, just show the URL
63
+ console.log(`\nšŸ”— Please open this URL in your browser:`);
64
+ console.log(` ${url}`);
65
+ }
66
+ }
67
+ /**
68
+ * Poll for credits balance increase
69
+ */
70
+ async function pollForPurchase(initialBalance, maxAttempts = 60, intervalMs = 2000) {
71
+ console.log('\nā³ Waiting for purchase confirmation...');
72
+ console.log(' (This may take a minute. Press Ctrl+C to cancel)');
73
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
74
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
75
+ try {
76
+ const status = await getBalance();
77
+ if (status.balance > initialBalance) {
78
+ return status.balance - initialBalance;
79
+ }
80
+ // Show progress indicator
81
+ if (attempt % 5 === 0 && attempt > 0) {
82
+ process.stdout.write('.');
83
+ }
84
+ }
85
+ catch (error) {
86
+ // Continue polling even if there's an error
87
+ continue;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ /**
93
+ * Handle the buy-credits command
94
+ */
95
+ async function handleBuyCredits(options) {
96
+ const startTime = Date.now();
97
+ let success = false;
98
+ let error;
99
+ try {
100
+ const config = await (0, user_config_1.getConfig)();
101
+ if (!config.token) {
102
+ console.error('āŒ Authentication required');
103
+ console.log('\nšŸ’” Please login first:');
104
+ console.log(' prpm login');
105
+ process.exit(1);
106
+ }
107
+ // Get current balance
108
+ console.log('šŸ” Checking current credits balance...');
109
+ const initialStatus = await getBalance();
110
+ console.log(` Current balance: ${initialStatus.balance} credits`);
111
+ console.log('\nšŸ’° Available credit packages:');
112
+ console.log(' Small: 100 credits - $5.00 ($0.05 per credit)');
113
+ console.log(' Medium: 250 credits - $11.25 ($0.045 per credit) - 10% savings');
114
+ console.log(' Large: 600 credits - $24.00 ($0.04 per credit) - 20% savings');
115
+ console.log('\n✨ These credits never expire!');
116
+ console.log('šŸ’” Tip: Subscribe to PRPM+ for 100 monthly credits at just $6/month');
117
+ // Build URL with package parameter if specified
118
+ const webappUrl = (0, webapp_url_1.getWebappUrl)(config.registryUrl || 'https://registry.prpm.dev');
119
+ let purchaseUrl = `${webappUrl}/playground/credits/buy`;
120
+ if (options.package) {
121
+ const validPackages = ['small', 'medium', 'large'];
122
+ if (!validPackages.includes(options.package)) {
123
+ console.error(`\nāŒ Invalid package: ${options.package}`);
124
+ console.log(' Valid options: small, medium, large');
125
+ process.exit(1);
126
+ }
127
+ purchaseUrl += `?package=${options.package}`;
128
+ }
129
+ // Open purchase page
130
+ console.log(`\n🌐 Opening purchase page in your browser...`);
131
+ await openBrowser(purchaseUrl);
132
+ // Poll for purchase confirmation
133
+ const creditsAdded = await pollForPurchase(initialStatus.balance);
134
+ if (creditsAdded !== null) {
135
+ const updatedStatus = await getBalance();
136
+ console.log('\n\nšŸŽ‰ Successfully purchased credits!');
137
+ console.log('\nšŸ“Š Credits added:');
138
+ console.log(` + ${creditsAdded} credits`);
139
+ console.log(` šŸ’³ New balance: ${updatedStatus.balance} credits`);
140
+ console.log('\nāœ… You can now:');
141
+ console.log(' - Test packages: prpm playground <package> "<input>"');
142
+ console.log(' - Check balance: prpm credits');
143
+ console.log(' - View history: prpm credits --history');
144
+ success = true;
145
+ }
146
+ else {
147
+ console.log('\n\nā±ļø Purchase process timed out or was canceled.');
148
+ console.log('\nšŸ’” If you completed the purchase, run this to verify:');
149
+ console.log(' prpm credits');
150
+ console.log('\nšŸ’” Or check your transaction history:');
151
+ console.log(' prpm credits --history');
152
+ }
153
+ }
154
+ catch (err) {
155
+ error = err instanceof Error ? err.message : String(err);
156
+ console.error(`\nāŒ Purchase failed: ${error}`);
157
+ process.exit(1);
158
+ }
159
+ finally {
160
+ await telemetry_1.telemetry.track({
161
+ command: 'buy-credits',
162
+ success,
163
+ error,
164
+ duration: Date.now() - startTime,
165
+ data: {
166
+ package: options.package,
167
+ },
168
+ });
169
+ await telemetry_1.telemetry.shutdown();
170
+ }
171
+ }
172
+ /**
173
+ * Create the buy-credits command
174
+ */
175
+ function createBuyCreditsCommand() {
176
+ const command = new commander_1.Command('buy-credits');
177
+ command
178
+ .description('Purchase one-time playground credits (never expire)')
179
+ .option('-p, --package <package>', 'Credit package to purchase (small, medium, large)')
180
+ .addHelpText('after', `
181
+ Credit Packages:
182
+ Small: 100 credits - $5.00 ($0.05 per credit)
183
+ Medium: 250 credits - $11.25 ($0.045 per credit) - 10% savings
184
+ Large: 600 credits - $24.00 ($0.04 per credit) - 20% savings
185
+
186
+ Credits Usage:
187
+ - Testing packages in playground uses 1-5 credits per request
188
+ - Token-based pricing: 1 credit = 5,000 tokens
189
+ - Model multipliers apply (Opus 5x, GPT-4o 2x, etc.)
190
+ - Credits never expire
191
+
192
+ How it works:
193
+ 1. Opens purchase page in your browser
194
+ 2. Select package and complete payment with Stripe
195
+ 3. Credits are added to your account immediately
196
+ 4. Start testing packages right away
197
+
198
+ Examples:
199
+ # Browse all packages
200
+ $ prpm buy-credits
201
+
202
+ # Pre-select a specific package
203
+ $ prpm buy-credits --package small
204
+ $ prpm buy-credits --package medium
205
+ $ prpm buy-credits --package large
206
+
207
+ # After purchase, check balance
208
+ $ prpm credits
209
+
210
+ # Test packages in playground
211
+ $ prpm playground @user/prompt "test input"
212
+
213
+ šŸ’” Better Value:
214
+ Subscribe to PRPM+ for 100 monthly credits at just $6/month
215
+ Run: prpm subscribe
216
+
217
+ Note: Purchased credits are one-time and never expire, unlike monthly credits.
218
+ `)
219
+ .action(async (options) => {
220
+ await handleBuyCredits(options);
221
+ process.exit(0);
222
+ });
223
+ return command;
224
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * Credits command - Check and manage playground credits
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleCredits = handleCredits;
7
+ exports.createCreditsCommand = createCreditsCommand;
8
+ const commander_1 = require("commander");
9
+ const user_config_1 = require("../core/user-config");
10
+ const telemetry_1 = require("../core/telemetry");
11
+ /**
12
+ * Make authenticated API call
13
+ */
14
+ async function apiCall(endpoint) {
15
+ const config = await (0, user_config_1.getConfig)();
16
+ const baseUrl = (config.registryUrl || "https://registry.prpm.dev").replace(/\/$/, '');
17
+ if (!config.token) {
18
+ throw new Error('Authentication required. Please run `prpm login` first.');
19
+ }
20
+ const response = await fetch(`${baseUrl}${endpoint}`, {
21
+ headers: {
22
+ Authorization: `Bearer ${config.token}`,
23
+ },
24
+ });
25
+ if (!response.ok) {
26
+ const errorData = await response.json().catch(() => ({}));
27
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
28
+ }
29
+ return response;
30
+ }
31
+ /**
32
+ * Display credits balance
33
+ */
34
+ async function showBalance() {
35
+ console.log('šŸ’³ Fetching playground credits balance...\n');
36
+ const response = await apiCall('/api/v1/playground/credits');
37
+ const balance = await response.json();
38
+ console.log('═'.repeat(5));
39
+ console.log(' PLAYGROUND CREDITS BALANCE');
40
+ console.log('═'.repeat(5));
41
+ // Total balance
42
+ console.log(`\nšŸ’° Total Balance: ${balance.balance} credits`);
43
+ // Breakdown
44
+ console.log('\nšŸ“Š Breakdown:');
45
+ if (balance.monthly_credits > 0) {
46
+ const monthlyRemaining = balance.monthly_credits - balance.monthly_credits_used;
47
+ console.log(` Monthly (PRPM+): ${monthlyRemaining}/${balance.monthly_credits} credits`);
48
+ if (balance.monthly_reset_at) {
49
+ const resetDate = new Date(balance.monthly_reset_at).toLocaleDateString();
50
+ console.log(` (Resets: ${resetDate})`);
51
+ }
52
+ }
53
+ if (balance.rollover_credits > 0) {
54
+ console.log(` Rollover: ${balance.rollover_credits} credits`);
55
+ if (balance.rollover_expires_at) {
56
+ const expiresDate = new Date(balance.rollover_expires_at).toLocaleDateString();
57
+ console.log(` (Expires: ${expiresDate})`);
58
+ }
59
+ }
60
+ if (balance.purchased_credits > 0) {
61
+ console.log(` Purchased: ${balance.purchased_credits} credits`);
62
+ console.log(` (Never expire)`);
63
+ }
64
+ // PRPM+ Status
65
+ if (balance.prpm_plus_status === 'active') {
66
+ console.log('\n✨ PRPM+ Active - You get 100 monthly credits!');
67
+ }
68
+ else {
69
+ console.log('\nšŸ’” Upgrade to PRPM+ for 100 monthly credits');
70
+ console.log(' Visit: https://prpm.dev/pricing');
71
+ }
72
+ console.log('\n═'.repeat(5));
73
+ }
74
+ /**
75
+ * Display recent credit transactions
76
+ */
77
+ async function showHistory(limit = 10) {
78
+ console.log(`šŸ’³ Fetching recent credit transactions...\n`);
79
+ const response = await apiCall(`/api/v1/playground/credits/history?limit=${limit}`);
80
+ const data = await response.json();
81
+ if (data.transactions.length === 0) {
82
+ console.log('No transaction history yet.');
83
+ return;
84
+ }
85
+ console.log('═'.repeat(5));
86
+ console.log(' CREDIT TRANSACTION HISTORY');
87
+ console.log('═'.repeat(5));
88
+ console.log();
89
+ for (const tx of data.transactions) {
90
+ const date = new Date(tx.created_at).toLocaleString();
91
+ const amount = tx.amount >= 0 ? `+${tx.amount}` : `${tx.amount}`;
92
+ const emoji = tx.amount >= 0 ? 'šŸ’°' : 'šŸ’ø';
93
+ console.log(`${emoji} ${date}`);
94
+ console.log(` Type: ${tx.transaction_type}`);
95
+ console.log(` Amount: ${amount} credits`);
96
+ console.log(` Balance: ${tx.balance_after} credits`);
97
+ console.log(` Details: ${tx.description}`);
98
+ console.log();
99
+ }
100
+ console.log('═'.repeat(5));
101
+ }
102
+ /**
103
+ * Handle the credits command
104
+ */
105
+ async function handleCredits(options) {
106
+ const startTime = Date.now();
107
+ let success = false;
108
+ let error;
109
+ try {
110
+ const config = await (0, user_config_1.getConfig)();
111
+ if (!config.token) {
112
+ console.error('āŒ Authentication required');
113
+ console.log('\nšŸ’” Please login first:');
114
+ console.log(' prpm login');
115
+ process.exit(1);
116
+ }
117
+ if (options.history) {
118
+ await showHistory(options.limit || 10);
119
+ }
120
+ else {
121
+ await showBalance();
122
+ }
123
+ console.log('\nšŸ’” Tips:');
124
+ console.log(' - Test packages: prpm playground <package> "<input>"');
125
+ console.log(' - View history: prpm credits --history');
126
+ console.log(' - Purchase credits: prpm buy-credits');
127
+ console.log(' - Subscribe to PRPM+: prpm subscribe');
128
+ success = true;
129
+ }
130
+ catch (err) {
131
+ error = err instanceof Error ? err.message : String(err);
132
+ console.error(`\nāŒ Failed to fetch credits: ${error}`);
133
+ process.exit(1);
134
+ }
135
+ finally {
136
+ await telemetry_1.telemetry.track({
137
+ command: 'credits',
138
+ success,
139
+ error,
140
+ duration: Date.now() - startTime,
141
+ data: {
142
+ showHistory: options.history || false,
143
+ },
144
+ });
145
+ await telemetry_1.telemetry.shutdown();
146
+ }
147
+ }
148
+ /**
149
+ * Create the credits command
150
+ */
151
+ function createCreditsCommand() {
152
+ const command = new commander_1.Command('credits');
153
+ command
154
+ .description('Check playground credits balance and transaction history')
155
+ .option('-h, --history', 'Show transaction history instead of balance', false)
156
+ .option('-l, --limit <number>', 'Number of transactions to show in history', '10')
157
+ .addHelpText('after', `
158
+ Examples:
159
+ # Check current balance
160
+ $ prpm credits
161
+
162
+ # View transaction history
163
+ $ prpm credits --history
164
+
165
+ # View last 20 transactions
166
+ $ prpm credits --history --limit 20
167
+
168
+ Credits are used for:
169
+ - Testing packages in the playground
170
+ - Running prompts with AI models
171
+ - Comparing packages against baselines
172
+
173
+ Get more credits:
174
+ - PRPM+ subscribers get 100 monthly credits
175
+ - Purchase additional credits at https://prpm.dev/playground/credits/buy
176
+ - Free tier gets 5 trial credits
177
+ `)
178
+ .action(async (options) => {
179
+ await handleCredits({
180
+ history: options.history,
181
+ limit: options.limit ? parseInt(options.limit, 10) : undefined,
182
+ });
183
+ process.exit(0);
184
+ });
185
+ return command;
186
+ }
@@ -220,8 +220,20 @@ async function handleInstall(packageSpec, options) {
220
220
  console.log(` ${pkg.name} ${pkg.official ? 'šŸ…' : ''}`);
221
221
  console.log(` ${pkg.description || 'No description'}`);
222
222
  console.log(` ${typeIcon} Type: ${typeLabel}`);
223
- // Determine format preference
224
- let format = options.as || pkg.format;
223
+ // Determine format preference with auto-detection
224
+ let format = options.as;
225
+ // Auto-detect format if not explicitly specified
226
+ if (!format) {
227
+ const detectedFormat = await (0, filesystem_1.autoDetectFormat)();
228
+ if (detectedFormat) {
229
+ format = detectedFormat;
230
+ console.log(` šŸ” Auto-detected ${format} format (found .${format}/ directory)`);
231
+ }
232
+ else {
233
+ // No existing directories found, use package's native format
234
+ format = pkg.format;
235
+ }
236
+ }
225
237
  // Special handling for Claude packages: default to CLAUDE.md if it doesn't exist
226
238
  // BUT only for packages that are generic rules (not skills, agents, or commands)
227
239
  if (!options.as && pkg.format === 'claude' && pkg.subtype === 'rule') {
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ /**
3
+ * Playground command - Test packages with AI models
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.handlePlayground = handlePlayground;
40
+ exports.createPlaygroundCommand = createPlaygroundCommand;
41
+ const commander_1 = require("commander");
42
+ const user_config_1 = require("../core/user-config");
43
+ const telemetry_1 = require("../core/telemetry");
44
+ const readline = __importStar(require("readline"));
45
+ /**
46
+ * Create a readline interface for user input
47
+ */
48
+ function createReadline() {
49
+ return readline.createInterface({
50
+ input: process.stdin,
51
+ output: process.stdout,
52
+ });
53
+ }
54
+ /**
55
+ * Prompt user for input
56
+ */
57
+ function prompt(rl, question) {
58
+ return new Promise((resolve) => {
59
+ rl.question(question, (answer) => {
60
+ resolve(answer);
61
+ });
62
+ });
63
+ }
64
+ /**
65
+ * Make authenticated API call to registry
66
+ */
67
+ async function apiCall(endpoint, method = 'GET', body) {
68
+ const config = await (0, user_config_1.getConfig)();
69
+ const baseUrl = (config.registryUrl || "https://registry.prpm.dev").replace(/\/$/, '');
70
+ if (!config.token) {
71
+ throw new Error('Authentication required. Please run `prpm login` first.');
72
+ }
73
+ const response = await fetch(`${baseUrl}${endpoint}`, {
74
+ method,
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ Authorization: `Bearer ${config.token}`,
78
+ },
79
+ body: body ? JSON.stringify(body) : undefined,
80
+ });
81
+ if (!response.ok) {
82
+ const errorData = await response.json().catch(() => ({}));
83
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
84
+ }
85
+ return response;
86
+ }
87
+ /**
88
+ * Resolve package name to UUID
89
+ */
90
+ async function resolvePackageId(packageName) {
91
+ // If it's already a UUID, return it
92
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
93
+ if (uuidRegex.test(packageName)) {
94
+ return packageName;
95
+ }
96
+ // Search for the package by name
97
+ const config = await (0, user_config_1.getConfig)();
98
+ const baseUrl = (config.registryUrl || "https://registry.prpm.dev").replace(/\/$/, '');
99
+ const response = await fetch(`${baseUrl}/api/v1/search?q=${encodeURIComponent(packageName)}&limit=10`);
100
+ if (!response.ok) {
101
+ throw new Error(`Failed to search for package: ${response.statusText}`);
102
+ }
103
+ const data = await response.json();
104
+ // Find exact match
105
+ const exactMatch = data.packages.find(pkg => pkg.name === packageName);
106
+ if (exactMatch) {
107
+ return exactMatch.id;
108
+ }
109
+ // If no exact match, throw error
110
+ throw new Error(`Package not found: ${packageName}`);
111
+ }
112
+ /**
113
+ * Execute a playground run
114
+ */
115
+ async function runPlayground(packageName, input, options, sessionId) {
116
+ // Resolve package name to UUID
117
+ const packageId = await resolvePackageId(packageName);
118
+ const response = await apiCall('/api/v1/playground/run', 'POST', {
119
+ package_id: packageId,
120
+ package_version: options.version,
121
+ input,
122
+ model: options.model || 'sonnet',
123
+ use_no_prompt: options.compare || false,
124
+ session_id: sessionId,
125
+ });
126
+ return response.json();
127
+ }
128
+ /**
129
+ * Format and display playground response
130
+ */
131
+ function displayResponse(result, showStats = true) {
132
+ // Get the latest assistant response
133
+ const lastMessage = result.conversation[result.conversation.length - 1];
134
+ if (lastMessage?.role === 'assistant') {
135
+ console.log('\n' + '─'.repeat(60));
136
+ console.log('šŸ¤– Assistant:');
137
+ console.log('─'.repeat(60));
138
+ console.log(lastMessage.content);
139
+ console.log('─'.repeat(60));
140
+ }
141
+ if (showStats) {
142
+ console.log(`\nšŸ“Š Stats:`);
143
+ console.log(` Model: ${result.model}`);
144
+ console.log(` Tokens: ${result.tokens_used.toLocaleString()}`);
145
+ console.log(` Credits spent: ${result.credits_spent}`);
146
+ console.log(` Credits remaining: ${result.credits_remaining}`);
147
+ console.log(` Duration: ${result.duration_ms}ms`);
148
+ }
149
+ }
150
+ /**
151
+ * Run interactive playground session
152
+ */
153
+ async function runInteractive(packageName, options) {
154
+ console.log('\nšŸŽ® Interactive Playground Mode');
155
+ console.log(` Package: ${packageName}`);
156
+ console.log(` Model: ${options.model || 'sonnet'}`);
157
+ if (options.compare) {
158
+ console.log(` Mode: Comparing against no prompt (raw model baseline)`);
159
+ }
160
+ console.log(` Type 'exit' or 'quit' to end session\n`);
161
+ const rl = createReadline();
162
+ let sessionId;
163
+ let turnCount = 0;
164
+ try {
165
+ while (true) {
166
+ const input = await prompt(rl, `\nšŸ’¬ You: `);
167
+ if (input.trim().toLowerCase() === 'exit' || input.trim().toLowerCase() === 'quit') {
168
+ console.log('\nšŸ‘‹ Ending playground session. Goodbye!');
169
+ break;
170
+ }
171
+ if (!input.trim()) {
172
+ console.log('āŒ Please enter a message');
173
+ continue;
174
+ }
175
+ try {
176
+ console.log('\nā³ Processing...');
177
+ const result = await runPlayground(packageName, input, options, sessionId);
178
+ // Store session ID for conversation continuity
179
+ sessionId = result.session_id;
180
+ turnCount++;
181
+ displayResponse(result, true);
182
+ }
183
+ catch (error) {
184
+ console.error(`\nāŒ Error: ${error instanceof Error ? error.message : String(error)}`);
185
+ if (error instanceof Error && error.message.includes('Insufficient credits')) {
186
+ console.log('\nšŸ’” Get more credits:');
187
+ console.log(' - Purchase credits: prpm buy-credits');
188
+ console.log(' - Subscribe to PRPM+: prpm subscribe');
189
+ console.log(' - Check balance: prpm credits');
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ if (turnCount > 0) {
195
+ console.log(`\nšŸ“ Session summary: ${turnCount} turn(s)`);
196
+ }
197
+ }
198
+ finally {
199
+ rl.close();
200
+ }
201
+ }
202
+ /**
203
+ * Run single playground query
204
+ */
205
+ async function runSingle(packageName, input, options) {
206
+ console.log(`\nšŸŽ® Testing package: ${packageName}`);
207
+ console.log(` Model: ${options.model || 'sonnet'}`);
208
+ if (options.compare) {
209
+ console.log(` Mode: Comparing with package vs. without (baseline)`);
210
+ }
211
+ try {
212
+ if (options.compare) {
213
+ // Comparison mode: run both with package and without
214
+ console.log('\nā³ Processing comparison (2 requests)...');
215
+ // Run with package (without use_no_prompt flag)
216
+ const withPackageOptions = { ...options, compare: false };
217
+ const resultWithPackage = await runPlayground(packageName, input, withPackageOptions);
218
+ // Run without package (with use_no_prompt flag)
219
+ const withoutPackageOptions = { ...options, compare: true };
220
+ const resultWithoutPackage = await runPlayground(packageName, input, withoutPackageOptions);
221
+ // Display both results
222
+ console.log('\n' + '═'.repeat(60));
223
+ console.log('šŸ“¦ WITH PACKAGE PROMPT');
224
+ console.log('═'.repeat(60));
225
+ displayResponse(resultWithPackage, false);
226
+ console.log('\n' + '═'.repeat(60));
227
+ console.log('šŸ”µ WITHOUT PACKAGE (BASELINE)');
228
+ console.log('═'.repeat(60));
229
+ displayResponse(resultWithoutPackage, false);
230
+ // Combined stats
231
+ console.log(`\nšŸ“Š Combined Stats:`);
232
+ console.log(` Total tokens: ${resultWithPackage.tokens_used + resultWithoutPackage.tokens_used}`);
233
+ console.log(` Total credits: ${resultWithPackage.credits_spent + resultWithoutPackage.credits_spent}`);
234
+ console.log(` Credits remaining: ${resultWithoutPackage.credits_remaining}`);
235
+ }
236
+ else {
237
+ // Single mode: run with package only
238
+ console.log('\nā³ Processing...');
239
+ const result = await runPlayground(packageName, input, options);
240
+ displayResponse(result, true);
241
+ }
242
+ console.log(`\nšŸ’” Tips:`);
243
+ console.log(` - Use --interactive for multi-turn conversation`);
244
+ console.log(` - Use --compare to test with and without the package prompt`);
245
+ console.log(` - Use --model to choose different models (sonnet, opus, gpt-4o, etc.)`);
246
+ }
247
+ catch (error) {
248
+ console.error(`\nāŒ Error: ${error instanceof Error ? error.message : String(error)}`);
249
+ if (error instanceof Error && error.message.includes('Insufficient credits')) {
250
+ console.log('\nšŸ’” Get more credits:');
251
+ console.log(' - Purchase credits: prpm buy-credits');
252
+ console.log(' - Subscribe to PRPM+: prpm subscribe');
253
+ console.log(' - Check balance: prpm credits');
254
+ }
255
+ process.exit(1);
256
+ }
257
+ }
258
+ /**
259
+ * Handle the playground command
260
+ */
261
+ async function handlePlayground(packageName, input, options) {
262
+ const startTime = Date.now();
263
+ let success = false;
264
+ let error;
265
+ try {
266
+ // Validate authentication
267
+ const config = await (0, user_config_1.getConfig)();
268
+ if (!config.token) {
269
+ console.error('āŒ Authentication required');
270
+ console.log('\nšŸ’” Please login first:');
271
+ console.log(' prpm login');
272
+ process.exit(1);
273
+ }
274
+ // Interactive mode or single query
275
+ if (options.interactive || !input) {
276
+ // Interactive mode
277
+ await runInteractive(packageName, options);
278
+ }
279
+ else {
280
+ // Single query mode
281
+ await runSingle(packageName, input, options);
282
+ }
283
+ success = true;
284
+ }
285
+ catch (err) {
286
+ error = err instanceof Error ? err.message : String(err);
287
+ console.error(`\nāŒ Playground execution failed: ${error}`);
288
+ process.exit(1);
289
+ }
290
+ finally {
291
+ await telemetry_1.telemetry.track({
292
+ command: 'playground',
293
+ success,
294
+ error,
295
+ duration: Date.now() - startTime,
296
+ data: {
297
+ packageName,
298
+ model: options.model || 'sonnet',
299
+ compare: options.compare || false,
300
+ interactive: options.interactive || false,
301
+ },
302
+ });
303
+ await telemetry_1.telemetry.shutdown();
304
+ }
305
+ }
306
+ /**
307
+ * Create the playground command
308
+ */
309
+ function createPlaygroundCommand() {
310
+ const command = new commander_1.Command('playground');
311
+ command
312
+ .description('Test a package with AI models in the playground')
313
+ .argument('<package>', 'Package name to test')
314
+ .argument('[input]', 'Input text to send to the model (omit for interactive mode)')
315
+ .option('-m, --model <model>', 'AI model to use (sonnet, opus, gpt-4o, gpt-4o-mini, gpt-4-turbo)', 'sonnet')
316
+ .option('-c, --compare', 'Compare against no prompt (test raw model baseline)', false)
317
+ .option('-i, --interactive', 'Start interactive multi-turn conversation mode', false)
318
+ .option('-v, --version <version>', 'Specific package version to test')
319
+ .addHelpText('after', `
320
+ Examples:
321
+ # Single query with default model (Sonnet)
322
+ $ prpm playground @anthropic/code-reviewer "Review this code: console.log('hello')"
323
+
324
+ # Interactive mode for multi-turn conversation
325
+ $ prpm playground @anthropic/brainstorm-assistant --interactive
326
+
327
+ # Compare with and without the package prompt
328
+ $ prpm playground @user/custom-prompt "Test input" --compare
329
+
330
+ # Use a different model
331
+ $ prpm playground @user/prompt --model opus "Complex task requiring Opus"
332
+ $ prpm playground @user/prompt --model gpt-4o "Test with GPT-4o"
333
+
334
+ # Test specific version
335
+ $ prpm playground @user/prompt@1.2.0 "Test input"
336
+
337
+ Available Models:
338
+ sonnet - Claude 3.5 Sonnet (default, balanced performance)
339
+ opus - Claude 3 Opus (most capable, higher cost)
340
+ gpt-4o - GPT-4o (OpenAI's latest)
341
+ gpt-4o-mini - GPT-4o Mini (faster, cheaper)
342
+ gpt-4-turbo - GPT-4 Turbo
343
+
344
+ Note: Playground usage requires credits. Run 'prpm credits' to check balance.
345
+ `)
346
+ .action(async (packageName, input, options) => {
347
+ await handlePlayground(packageName, input, options);
348
+ process.exit(0);
349
+ });
350
+ return command;
351
+ }
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * Subscribe command - Subscribe to PRPM+ for monthly credits
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleSubscribe = handleSubscribe;
7
+ exports.createSubscribeCommand = createSubscribeCommand;
8
+ const commander_1 = require("commander");
9
+ const user_config_1 = require("../core/user-config");
10
+ const telemetry_1 = require("../core/telemetry");
11
+ const child_process_1 = require("child_process");
12
+ const util_1 = require("util");
13
+ const webapp_url_1 = require("../utils/webapp-url");
14
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
15
+ /**
16
+ * Make authenticated API call
17
+ */
18
+ async function apiCall(endpoint) {
19
+ const config = await (0, user_config_1.getConfig)();
20
+ const baseUrl = (config.registryUrl || "https://registry.prpm.dev").replace(/\/$/, '');
21
+ if (!config.token) {
22
+ throw new Error('Authentication required. Please run `prpm login` first.');
23
+ }
24
+ const response = await fetch(`${baseUrl}${endpoint}`, {
25
+ headers: {
26
+ Authorization: `Bearer ${config.token}`,
27
+ },
28
+ });
29
+ if (!response.ok) {
30
+ const errorData = await response.json().catch(() => ({}));
31
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
32
+ }
33
+ return response;
34
+ }
35
+ /**
36
+ * Get current subscription status
37
+ */
38
+ async function getSubscriptionStatus() {
39
+ const response = await apiCall('/api/v1/playground/credits');
40
+ return response.json();
41
+ }
42
+ /**
43
+ * Open URL in default browser
44
+ */
45
+ async function openBrowser(url) {
46
+ const platform = process.platform;
47
+ let command;
48
+ if (platform === 'darwin') {
49
+ command = `open "${url}"`;
50
+ }
51
+ else if (platform === 'win32') {
52
+ command = `start "" "${url}"`;
53
+ }
54
+ else {
55
+ // Linux and other Unix-like systems
56
+ command = `xdg-open "${url}"`;
57
+ }
58
+ try {
59
+ await execAsync(command);
60
+ }
61
+ catch (error) {
62
+ // If automatic opening fails, just show the URL
63
+ console.log(`\nšŸ”— Please open this URL in your browser:`);
64
+ console.log(` ${url}`);
65
+ }
66
+ }
67
+ /**
68
+ * Poll for subscription status change
69
+ */
70
+ async function pollForSubscription(initialStatus, maxAttempts = 60, intervalMs = 2000) {
71
+ console.log('\nā³ Waiting for subscription confirmation...');
72
+ console.log(' (This may take a minute. Press Ctrl+C to cancel)');
73
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
74
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
75
+ try {
76
+ const status = await getSubscriptionStatus();
77
+ if (status.prpm_plus_status === 'active' && initialStatus !== 'active') {
78
+ return true;
79
+ }
80
+ // Show progress indicator
81
+ if (attempt % 5 === 0 && attempt > 0) {
82
+ process.stdout.write('.');
83
+ }
84
+ }
85
+ catch (error) {
86
+ // Continue polling even if there's an error
87
+ continue;
88
+ }
89
+ }
90
+ return false;
91
+ }
92
+ /**
93
+ * Handle the subscribe command
94
+ */
95
+ async function handleSubscribe() {
96
+ const startTime = Date.now();
97
+ let success = false;
98
+ let error;
99
+ try {
100
+ const config = await (0, user_config_1.getConfig)();
101
+ if (!config.token) {
102
+ console.error('āŒ Authentication required');
103
+ console.log('\nšŸ’” Please login first:');
104
+ console.log(' prpm login');
105
+ process.exit(1);
106
+ }
107
+ // Get current status
108
+ console.log('šŸ” Checking current subscription status...');
109
+ const initialStatus = await getSubscriptionStatus();
110
+ if (initialStatus.prpm_plus_status === 'active') {
111
+ console.log('\nāœ… You are already subscribed to PRPM+!');
112
+ console.log(`\nšŸ“Š Current benefits:`);
113
+ console.log(` šŸ’° Monthly credits: ${initialStatus.monthly_credits}`);
114
+ console.log(` šŸ“¦ Priority support`);
115
+ console.log(` šŸš€ Early access to new features`);
116
+ console.log('\nšŸ’” Manage your subscription at:');
117
+ console.log(' https://prpm.dev/settings/billing');
118
+ process.exit(0);
119
+ }
120
+ console.log('\n✨ Subscribe to PRPM+ and get:');
121
+ console.log(' šŸ’° 100 monthly playground credits');
122
+ console.log(' ā™»ļø Rollover unused credits (up to 200)');
123
+ console.log(' šŸ“¦ Priority support');
124
+ console.log(' šŸš€ Early access to new features');
125
+ console.log('\nšŸ’µ Pricing:');
126
+ console.log(' $6/month for individuals');
127
+ console.log(' $3/month for verified organization members (50% off)');
128
+ // Open subscription page
129
+ const webappUrl = (0, webapp_url_1.getWebappUrl)(config.registryUrl || 'https://registry.prpm.dev');
130
+ const subscribeUrl = `${webappUrl}/playground/credits/subscribe`;
131
+ console.log(`\n🌐 Opening subscription page in your browser...`);
132
+ await openBrowser(subscribeUrl);
133
+ // Poll for subscription confirmation
134
+ const subscribed = await pollForSubscription(initialStatus.prpm_plus_status);
135
+ if (subscribed) {
136
+ const updatedStatus = await getSubscriptionStatus();
137
+ console.log('\n\nšŸŽ‰ Successfully subscribed to PRPM+!');
138
+ console.log('\nšŸ“Š Your benefits:');
139
+ console.log(` šŸ’° Monthly credits: ${updatedStatus.monthly_credits}`);
140
+ console.log(` šŸ’³ Current balance: ${updatedStatus.balance} credits`);
141
+ console.log('\nāœ… You can now:');
142
+ console.log(' - Test packages in playground: prpm playground <package> "<input>"');
143
+ console.log(' - Check credits anytime: prpm credits');
144
+ success = true;
145
+ }
146
+ else {
147
+ console.log('\n\nā±ļø Subscription process timed out or was canceled.');
148
+ console.log('\nšŸ’” If you completed the subscription, run this to verify:');
149
+ console.log(' prpm credits');
150
+ console.log('\nšŸ’” Or visit your account settings:');
151
+ console.log(' https://prpm.dev/settings/billing');
152
+ }
153
+ }
154
+ catch (err) {
155
+ error = err instanceof Error ? err.message : String(err);
156
+ console.error(`\nāŒ Subscription failed: ${error}`);
157
+ process.exit(1);
158
+ }
159
+ finally {
160
+ await telemetry_1.telemetry.track({
161
+ command: 'subscribe',
162
+ success,
163
+ error,
164
+ duration: Date.now() - startTime,
165
+ });
166
+ await telemetry_1.telemetry.shutdown();
167
+ }
168
+ }
169
+ /**
170
+ * Create the subscribe command
171
+ */
172
+ function createSubscribeCommand() {
173
+ const command = new commander_1.Command('subscribe');
174
+ command
175
+ .description('Subscribe to PRPM+ for monthly playground credits and benefits')
176
+ .addHelpText('after', `
177
+ PRPM+ Benefits:
178
+ ✨ 100 monthly playground credits (worth $6+ in API costs)
179
+ ā™»ļø Rollover unused credits up to 200 (1-month expiry)
180
+ šŸ“¦ Priority support and bug fixes
181
+ šŸš€ Early access to new features
182
+ šŸ’¬ Access to PRPM+ community
183
+
184
+ Pricing:
185
+ Individual: $6/month
186
+ Organization: $3/month (50% discount for verified org members)
187
+
188
+ How it works:
189
+ 1. Opens subscription page in your browser
190
+ 2. Complete payment with Stripe
191
+ 3. Credits are added automatically
192
+ 4. Start testing packages immediately
193
+
194
+ Examples:
195
+ # Subscribe to PRPM+
196
+ $ prpm subscribe
197
+
198
+ # After subscribing, check your credits
199
+ $ prpm credits
200
+
201
+ # Test packages in playground
202
+ $ prpm playground @anthropic/assistant "Help me brainstorm ideas"
203
+
204
+ Note: You can cancel anytime from https://prpm.dev/settings/billing
205
+ `)
206
+ .action(async () => {
207
+ await handleSubscribe();
208
+ process.exit(0);
209
+ });
210
+ return command;
211
+ }
@@ -11,6 +11,8 @@ exports.ensureDirectoryExists = ensureDirectoryExists;
11
11
  exports.saveFile = saveFile;
12
12
  exports.deleteFile = deleteFile;
13
13
  exports.fileExists = fileExists;
14
+ exports.directoryExists = directoryExists;
15
+ exports.autoDetectFormat = autoDetectFormat;
14
16
  exports.generateId = generateId;
15
17
  exports.stripAuthorNamespace = stripAuthorNamespace;
16
18
  exports.getInstalledFilePath = getInstalledFilePath;
@@ -119,6 +121,39 @@ async function fileExists(filePath) {
119
121
  return false;
120
122
  }
121
123
  }
124
+ /**
125
+ * Check if a directory exists
126
+ */
127
+ async function directoryExists(dirPath) {
128
+ try {
129
+ const stats = await fs_1.promises.stat(dirPath);
130
+ return stats.isDirectory();
131
+ }
132
+ catch {
133
+ return false;
134
+ }
135
+ }
136
+ /**
137
+ * Auto-detect the format based on existing directories in the current project
138
+ * Returns the format if a matching directory is found, or null if none found
139
+ */
140
+ async function autoDetectFormat() {
141
+ const formatDirs = [
142
+ { format: 'cursor', dir: '.cursor' },
143
+ { format: 'claude', dir: '.claude' },
144
+ { format: 'continue', dir: '.continue' },
145
+ { format: 'windsurf', dir: '.windsurf' },
146
+ { format: 'copilot', dir: '.github/instructions' },
147
+ { format: 'kiro', dir: '.kiro' },
148
+ { format: 'agents.md', dir: '.agents' },
149
+ ];
150
+ for (const { format, dir } of formatDirs) {
151
+ if (await directoryExists(dir)) {
152
+ return format;
153
+ }
154
+ }
155
+ return null;
156
+ }
122
157
  /**
123
158
  * Generate a unique ID from filename
124
159
  */
package/dist/index.js CHANGED
@@ -27,6 +27,10 @@ const schema_1 = require("./commands/schema");
27
27
  const init_1 = require("./commands/init");
28
28
  const config_1 = require("./commands/config");
29
29
  const catalog_1 = require("./commands/catalog");
30
+ const playground_1 = require("./commands/playground");
31
+ const credits_1 = require("./commands/credits");
32
+ const subscribe_1 = require("./commands/subscribe");
33
+ const buy_credits_1 = require("./commands/buy-credits");
30
34
  const telemetry_2 = require("./core/telemetry");
31
35
  // Read version from package.json
32
36
  function getVersion() {
@@ -65,6 +69,11 @@ program.addCommand((0, list_1.createListCommand)());
65
69
  program.addCommand((0, uninstall_1.createUninstallCommand)());
66
70
  program.addCommand((0, index_1.createIndexCommand)());
67
71
  program.addCommand((0, telemetry_1.createTelemetryCommand)());
72
+ // Playground commands
73
+ program.addCommand((0, playground_1.createPlaygroundCommand)());
74
+ program.addCommand((0, credits_1.createCreditsCommand)());
75
+ program.addCommand((0, subscribe_1.createSubscribeCommand)());
76
+ program.addCommand((0, buy_credits_1.createBuyCreditsCommand)());
68
77
  // Utility commands
69
78
  program.addCommand((0, schema_1.createSchemaCommand)());
70
79
  program.addCommand((0, config_1.createConfigCommand)());
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Utility to convert registry URL to webapp URL
4
+ * Handles localhost, production, and custom registry URLs
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getWebappUrl = getWebappUrl;
8
+ /**
9
+ * Convert a registry URL to its corresponding webapp URL
10
+ *
11
+ * @param registryUrl - The registry URL (e.g., https://registry.prpm.dev)
12
+ * @returns The webapp URL (e.g., https://prpm.dev)
13
+ *
14
+ * @example
15
+ * // Production
16
+ * getWebappUrl('https://registry.prpm.dev') // => 'https://prpm.dev'
17
+ *
18
+ * // Local development
19
+ * getWebappUrl('http://localhost:3111') // => 'http://localhost:5173'
20
+ *
21
+ * // Custom registry
22
+ * getWebappUrl('https://registry.custom.com') // => 'https://custom.com'
23
+ */
24
+ function getWebappUrl(registryUrl) {
25
+ const cleanUrl = registryUrl.replace(/\/$/, '').replace(/\/api\/?$/, '');
26
+ if (cleanUrl.includes('localhost') || cleanUrl.includes('127.0.0.1')) {
27
+ // Local development: registry on port 3111, webapp on port 5173
28
+ return cleanUrl.replace(':3111', ':5173');
29
+ }
30
+ if (cleanUrl.includes('registry.prpm.dev')) {
31
+ // Production: always use prpm.dev webapp
32
+ return 'https://prpm.dev';
33
+ }
34
+ // Custom registry: assume webapp is on same host without 'registry.' subdomain
35
+ try {
36
+ const url = new URL(cleanUrl);
37
+ const hostname = url.hostname.replace(/^registry\./, '');
38
+ return `${url.protocol}//${hostname}`;
39
+ }
40
+ catch {
41
+ // If URL parsing fails, return as-is
42
+ return cleanUrl;
43
+ }
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Prompt Package Manager CLI - Install and manage prompt-based files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -45,8 +45,8 @@
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
47
  "@octokit/rest": "^22.0.0",
48
- "@pr-pm/registry-client": "^1.3.4",
49
- "@pr-pm/types": "^0.2.4",
48
+ "@pr-pm/registry-client": "^1.3.5",
49
+ "@pr-pm/types": "^0.2.6",
50
50
  "ajv": "^8.17.1",
51
51
  "ajv-formats": "^3.0.1",
52
52
  "commander": "^11.1.0",