prpm 0.1.5 → 0.1.6

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,223 @@
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 execAsync = (0, util_1.promisify)(child_process_1.exec);
14
+ /**
15
+ * Make authenticated API call
16
+ */
17
+ async function apiCall(endpoint) {
18
+ const config = await (0, user_config_1.getConfig)();
19
+ const baseUrl = (config.registryUrl || 'https://registry.prpm.dev').replace(/\/$/, '');
20
+ if (!config.token) {
21
+ throw new Error('Authentication required. Please run `prpm login` first.');
22
+ }
23
+ const response = await fetch(`${baseUrl}${endpoint}`, {
24
+ headers: {
25
+ Authorization: `Bearer ${config.token}`,
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ const errorData = await response.json().catch(() => ({}));
30
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
31
+ }
32
+ return response;
33
+ }
34
+ /**
35
+ * Get current credits balance
36
+ */
37
+ async function getBalance() {
38
+ const response = await apiCall('/api/v1/playground/credits');
39
+ return response.json();
40
+ }
41
+ /**
42
+ * Open URL in default browser
43
+ */
44
+ async function openBrowser(url) {
45
+ const platform = process.platform;
46
+ let command;
47
+ if (platform === 'darwin') {
48
+ command = `open "${url}"`;
49
+ }
50
+ else if (platform === 'win32') {
51
+ command = `start "" "${url}"`;
52
+ }
53
+ else {
54
+ // Linux and other Unix-like systems
55
+ command = `xdg-open "${url}"`;
56
+ }
57
+ try {
58
+ await execAsync(command);
59
+ }
60
+ catch (error) {
61
+ // If automatic opening fails, just show the URL
62
+ console.log(`\nšŸ”— Please open this URL in your browser:`);
63
+ console.log(` ${url}`);
64
+ }
65
+ }
66
+ /**
67
+ * Poll for credits balance increase
68
+ */
69
+ async function pollForPurchase(initialBalance, maxAttempts = 60, intervalMs = 2000) {
70
+ console.log('\nā³ Waiting for purchase confirmation...');
71
+ console.log(' (This may take a minute. Press Ctrl+C to cancel)');
72
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
73
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
74
+ try {
75
+ const status = await getBalance();
76
+ if (status.balance > initialBalance) {
77
+ return status.balance - initialBalance;
78
+ }
79
+ // Show progress indicator
80
+ if (attempt % 5 === 0 && attempt > 0) {
81
+ process.stdout.write('.');
82
+ }
83
+ }
84
+ catch (error) {
85
+ // Continue polling even if there's an error
86
+ continue;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+ /**
92
+ * Handle the buy-credits command
93
+ */
94
+ async function handleBuyCredits(options) {
95
+ const startTime = Date.now();
96
+ let success = false;
97
+ let error;
98
+ try {
99
+ const config = await (0, user_config_1.getConfig)();
100
+ if (!config.token) {
101
+ console.error('āŒ Authentication required');
102
+ console.log('\nšŸ’” Please login first:');
103
+ console.log(' prpm login');
104
+ process.exit(1);
105
+ }
106
+ // Get current balance
107
+ console.log('šŸ” Checking current credits balance...');
108
+ const initialStatus = await getBalance();
109
+ console.log(` Current balance: ${initialStatus.balance} credits`);
110
+ console.log('\nšŸ’° Available credit packages:');
111
+ console.log(' Small: 100 credits - $5.00 ($0.05 per credit)');
112
+ console.log(' Medium: 250 credits - $11.25 ($0.045 per credit) - 10% savings');
113
+ console.log(' Large: 600 credits - $24.00 ($0.04 per credit) - 20% savings');
114
+ console.log('\n✨ These credits never expire!');
115
+ console.log('šŸ’” Tip: Subscribe to PRPM+ for 100 monthly credits at just $6/month');
116
+ // Build URL with package parameter if specified
117
+ const baseUrl = (config.registryUrl || 'https://registry.prpm.dev').replace(/api\/?$/, '');
118
+ let purchaseUrl = `${baseUrl}/playground/credits/buy`;
119
+ if (options.package) {
120
+ const validPackages = ['small', 'medium', 'large'];
121
+ if (!validPackages.includes(options.package)) {
122
+ console.error(`\nāŒ Invalid package: ${options.package}`);
123
+ console.log(' Valid options: small, medium, large');
124
+ process.exit(1);
125
+ }
126
+ purchaseUrl += `?package=${options.package}`;
127
+ }
128
+ // Open purchase page
129
+ console.log(`\n🌐 Opening purchase page in your browser...`);
130
+ await openBrowser(purchaseUrl);
131
+ // Poll for purchase confirmation
132
+ const creditsAdded = await pollForPurchase(initialStatus.balance);
133
+ if (creditsAdded !== null) {
134
+ const updatedStatus = await getBalance();
135
+ console.log('\n\nšŸŽ‰ Successfully purchased credits!');
136
+ console.log('\nšŸ“Š Credits added:');
137
+ console.log(` + ${creditsAdded} credits`);
138
+ console.log(` šŸ’³ New balance: ${updatedStatus.balance} credits`);
139
+ console.log('\nāœ… You can now:');
140
+ console.log(' - Test packages: prpm playground <package> "<input>"');
141
+ console.log(' - Check balance: prpm credits');
142
+ console.log(' - View history: prpm credits --history');
143
+ success = true;
144
+ }
145
+ else {
146
+ console.log('\n\nā±ļø Purchase process timed out or was canceled.');
147
+ console.log('\nšŸ’” If you completed the purchase, run this to verify:');
148
+ console.log(' prpm credits');
149
+ console.log('\nšŸ’” Or check your transaction history:');
150
+ console.log(' prpm credits --history');
151
+ }
152
+ }
153
+ catch (err) {
154
+ error = err instanceof Error ? err.message : String(err);
155
+ console.error(`\nāŒ Purchase failed: ${error}`);
156
+ process.exit(1);
157
+ }
158
+ finally {
159
+ await telemetry_1.telemetry.track({
160
+ command: 'buy-credits',
161
+ success,
162
+ error,
163
+ duration: Date.now() - startTime,
164
+ data: {
165
+ package: options.package,
166
+ },
167
+ });
168
+ await telemetry_1.telemetry.shutdown();
169
+ }
170
+ }
171
+ /**
172
+ * Create the buy-credits command
173
+ */
174
+ function createBuyCreditsCommand() {
175
+ const command = new commander_1.Command('buy-credits');
176
+ command
177
+ .description('Purchase one-time playground credits (never expire)')
178
+ .option('-p, --package <package>', 'Credit package to purchase (small, medium, large)')
179
+ .addHelpText('after', `
180
+ Credit Packages:
181
+ Small: 100 credits - $5.00 ($0.05 per credit)
182
+ Medium: 250 credits - $11.25 ($0.045 per credit) - 10% savings
183
+ Large: 600 credits - $24.00 ($0.04 per credit) - 20% savings
184
+
185
+ Credits Usage:
186
+ - Testing packages in playground uses 1-5 credits per request
187
+ - Token-based pricing: 1 credit = 5,000 tokens
188
+ - Model multipliers apply (Opus 5x, GPT-4o 2x, etc.)
189
+ - Credits never expire
190
+
191
+ How it works:
192
+ 1. Opens purchase page in your browser
193
+ 2. Select package and complete payment with Stripe
194
+ 3. Credits are added to your account immediately
195
+ 4. Start testing packages right away
196
+
197
+ Examples:
198
+ # Browse all packages
199
+ $ prpm buy-credits
200
+
201
+ # Pre-select a specific package
202
+ $ prpm buy-credits --package small
203
+ $ prpm buy-credits --package medium
204
+ $ prpm buy-credits --package large
205
+
206
+ # After purchase, check balance
207
+ $ prpm credits
208
+
209
+ # Test packages in playground
210
+ $ prpm playground @user/prompt "test input"
211
+
212
+ šŸ’” Better Value:
213
+ Subscribe to PRPM+ for 100 monthly credits at just $6/month
214
+ Run: prpm subscribe
215
+
216
+ Note: Purchased credits are one-time and never expire, unlike monthly credits.
217
+ `)
218
+ .action(async (options) => {
219
+ await handleBuyCredits(options);
220
+ process.exit(0);
221
+ });
222
+ return command;
223
+ }
@@ -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
+ }
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ /**
3
+ * Hooks command - Manage git hooks for running agents
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createHooksCommand = createHooksCommand;
10
+ const commander_1 = require("commander");
11
+ const fs_1 = require("fs");
12
+ const path_1 = require("path");
13
+ const child_process_1 = require("child_process");
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ /**
16
+ * Check if we're in a git repository
17
+ */
18
+ function isGitRepo() {
19
+ try {
20
+ (0, child_process_1.execSync)('git rev-parse --git-dir', { stdio: 'ignore' });
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ /**
28
+ * Get the git hooks directory
29
+ */
30
+ function getGitHooksDir() {
31
+ try {
32
+ const gitDir = (0, child_process_1.execSync)('git rev-parse --git-dir', { encoding: 'utf-8' }).trim();
33
+ return (0, path_1.join)(gitDir, 'hooks');
34
+ }
35
+ catch (error) {
36
+ throw new Error('Not in a git repository');
37
+ }
38
+ }
39
+ /**
40
+ * Create hooks configuration file template
41
+ */
42
+ function createHooksConfig() {
43
+ const configDir = '.prpm';
44
+ const configPath = (0, path_1.join)(configDir, 'hooks.json');
45
+ if ((0, fs_1.existsSync)(configPath)) {
46
+ console.log(chalk_1.default.yellow(`āš ļø Hooks configuration already exists at ${configPath}`));
47
+ return;
48
+ }
49
+ // Create .prpm directory if it doesn't exist
50
+ if (!(0, fs_1.existsSync)(configDir)) {
51
+ (0, fs_1.mkdirSync)(configDir, { recursive: true });
52
+ }
53
+ const template = {
54
+ hooks: {
55
+ 'pre-commit': {
56
+ enabled: true,
57
+ agents: [
58
+ {
59
+ name: '@pre-commit/security-scanner',
60
+ files: '**/*.{js,ts,py}',
61
+ severity: 'error',
62
+ autoFix: false,
63
+ },
64
+ ],
65
+ },
66
+ },
67
+ settings: {
68
+ enabled: true,
69
+ timeout: 30000,
70
+ cache: {
71
+ enabled: true,
72
+ ttl: 3600,
73
+ },
74
+ },
75
+ };
76
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(template, null, 2));
77
+ console.log(chalk_1.default.green(`āœ“ Created hooks configuration at ${configPath}`));
78
+ console.log(chalk_1.default.dim('\nEdit this file to configure which agents run on git hooks.'));
79
+ console.log(chalk_1.default.dim('Install agents with: prpm install @pre-commit/security-scanner\n'));
80
+ }
81
+ /**
82
+ * Install git hooks
83
+ */
84
+ async function handleInstall() {
85
+ if (!isGitRepo()) {
86
+ console.error(chalk_1.default.red('āœ— Not in a git repository'));
87
+ console.log(chalk_1.default.dim('Run this command from the root of your git repository.\n'));
88
+ process.exit(1);
89
+ }
90
+ const hooksDir = getGitHooksDir();
91
+ const configPath = (0, path_1.join)('.prpm', 'hooks.json');
92
+ // Create hooks config if it doesn't exist
93
+ if (!(0, fs_1.existsSync)(configPath)) {
94
+ createHooksConfig();
95
+ }
96
+ // Create hooks directory if it doesn't exist
97
+ if (!(0, fs_1.existsSync)(hooksDir)) {
98
+ (0, fs_1.mkdirSync)(hooksDir, { recursive: true });
99
+ }
100
+ // Create pre-commit hook
101
+ const preCommitHook = (0, path_1.join)(hooksDir, 'pre-commit');
102
+ const hookScript = `#!/bin/sh
103
+ # PRPM pre-commit hook
104
+ # Generated by prpm hooks install
105
+
106
+ # Run PRPM hooks command
107
+ prpm hooks run pre-commit
108
+
109
+ # Exit with the same code as the hooks command
110
+ exit $?
111
+ `;
112
+ (0, fs_1.writeFileSync)(preCommitHook, hookScript);
113
+ (0, fs_1.chmodSync)(preCommitHook, 0o755);
114
+ console.log(chalk_1.default.green('āœ“ Git hooks installed successfully'));
115
+ console.log(chalk_1.default.dim(`\nInstalled hooks:`));
116
+ console.log(chalk_1.default.dim(` - pre-commit → ${preCommitHook}`));
117
+ console.log(chalk_1.default.dim(`\nConfiguration: ${configPath}`));
118
+ console.log(chalk_1.default.dim('\nRun ') + chalk_1.default.cyan('prpm hooks uninstall') + chalk_1.default.dim(' to remove hooks.\n'));
119
+ }
120
+ /**
121
+ * Uninstall git hooks
122
+ */
123
+ async function handleUninstall() {
124
+ if (!isGitRepo()) {
125
+ console.error(chalk_1.default.red('āœ— Not in a git repository'));
126
+ process.exit(1);
127
+ }
128
+ const hooksDir = getGitHooksDir();
129
+ const preCommitHook = (0, path_1.join)(hooksDir, 'pre-commit');
130
+ if ((0, fs_1.existsSync)(preCommitHook)) {
131
+ // Check if it's a PRPM hook
132
+ const content = (0, fs_1.readFileSync)(preCommitHook, 'utf-8');
133
+ if (content.includes('PRPM pre-commit hook')) {
134
+ (0, fs_1.unlinkSync)(preCommitHook);
135
+ console.log(chalk_1.default.green('āœ“ PRPM git hooks uninstalled successfully'));
136
+ console.log(chalk_1.default.dim(`\nRemoved: ${preCommitHook}\n`));
137
+ }
138
+ else {
139
+ console.log(chalk_1.default.yellow('āš ļø Pre-commit hook exists but was not installed by PRPM'));
140
+ console.log(chalk_1.default.dim('Will not remove it. Please remove manually if needed.\n'));
141
+ }
142
+ }
143
+ else {
144
+ console.log(chalk_1.default.yellow('āš ļø No PRPM hooks found'));
145
+ console.log(chalk_1.default.dim('Hooks may have already been removed.\n'));
146
+ }
147
+ }
148
+ /**
149
+ * Run hooks for a specific event (pre-commit, pre-push, etc.)
150
+ */
151
+ async function handleRun(hookType) {
152
+ const configPath = (0, path_1.join)('.prpm', 'hooks.json');
153
+ if (!(0, fs_1.existsSync)(configPath)) {
154
+ console.error(chalk_1.default.red('āœ— Hooks configuration not found'));
155
+ console.log(chalk_1.default.dim(`Run ${chalk_1.default.cyan('prpm hooks install')} to set up hooks.\n`));
156
+ process.exit(1);
157
+ }
158
+ // Read configuration
159
+ const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
160
+ // Check if hooks are globally enabled
161
+ if (config.settings?.enabled === false) {
162
+ console.log(chalk_1.default.dim('ā„¹ļø Hooks are disabled in configuration'));
163
+ process.exit(0);
164
+ }
165
+ // Get hook configuration
166
+ const hookConfig = config.hooks?.[hookType];
167
+ if (!hookConfig || !hookConfig.enabled) {
168
+ console.log(chalk_1.default.dim(`ā„¹ļø ${hookType} hook is not enabled`));
169
+ process.exit(0);
170
+ }
171
+ // Check if any agents are configured
172
+ if (!hookConfig.agents || hookConfig.agents.length === 0) {
173
+ console.log(chalk_1.default.dim(`ā„¹ļø No agents configured for ${hookType}`));
174
+ process.exit(0);
175
+ }
176
+ console.log(chalk_1.default.blue(`\nšŸ” Running ${hookType} checks...\n`));
177
+ // TODO: Implement agent execution
178
+ // For now, just show what would be executed
179
+ let hasErrors = false;
180
+ for (const agent of hookConfig.agents) {
181
+ if (!agent.enabled) {
182
+ continue;
183
+ }
184
+ console.log(chalk_1.default.dim(` Checking with ${agent.name}...`));
185
+ // TODO: Execute agent here
186
+ // This is a placeholder - actual implementation will:
187
+ // 1. Get staged files (for pre-commit) using git diff --cached --name-only
188
+ // 2. Filter files by glob pattern (agent.files)
189
+ // 3. Load agent package
190
+ // 4. Execute agent via Anthropic SDK
191
+ // 5. Process results and show output
192
+ console.log(chalk_1.default.yellow(` āš ļø Not implemented yet - would check files matching: ${agent.files}`));
193
+ if (agent.severity === 'error') {
194
+ // hasErrors = true; // Uncomment when implemented
195
+ }
196
+ }
197
+ if (hasErrors) {
198
+ console.log(chalk_1.default.red('\nāœ— Checks failed. Please fix the issues above.\n'));
199
+ process.exit(1);
200
+ }
201
+ console.log(chalk_1.default.green('\nāœ“ All checks passed\n'));
202
+ process.exit(0);
203
+ }
204
+ /**
205
+ * Show status of installed hooks
206
+ */
207
+ async function handleStatus() {
208
+ if (!isGitRepo()) {
209
+ console.error(chalk_1.default.red('āœ— Not in a git repository'));
210
+ process.exit(1);
211
+ }
212
+ const hooksDir = getGitHooksDir();
213
+ const configPath = (0, path_1.join)('.prpm', 'hooks.json');
214
+ const preCommitHook = (0, path_1.join)(hooksDir, 'pre-commit');
215
+ console.log(chalk_1.default.blue('\nšŸ“‹ PRPM Hooks Status\n'));
216
+ // Check if hooks are installed
217
+ let hooksInstalled = false;
218
+ if ((0, fs_1.existsSync)(preCommitHook)) {
219
+ const content = (0, fs_1.readFileSync)(preCommitHook, 'utf-8');
220
+ if (content.includes('PRPM pre-commit hook')) {
221
+ hooksInstalled = true;
222
+ console.log(chalk_1.default.green('āœ“ Git hooks: Installed'));
223
+ }
224
+ else {
225
+ console.log(chalk_1.default.yellow('āš ļø Git hooks: Other hook exists'));
226
+ }
227
+ }
228
+ else {
229
+ console.log(chalk_1.default.red('āœ— Git hooks: Not installed'));
230
+ }
231
+ // Check if config exists
232
+ if ((0, fs_1.existsSync)(configPath)) {
233
+ console.log(chalk_1.default.green(`āœ“ Configuration: ${configPath}`));
234
+ const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
235
+ const enabled = config.settings?.enabled !== false;
236
+ console.log(chalk_1.default.dim(` Enabled: ${enabled ? 'yes' : 'no'}`));
237
+ // Show configured hooks
238
+ if (config.hooks) {
239
+ const hookTypes = Object.keys(config.hooks);
240
+ if (hookTypes.length > 0) {
241
+ console.log(chalk_1.default.dim(`\n Configured hooks:`));
242
+ for (const hookType of hookTypes) {
243
+ const hookConfig = config.hooks[hookType];
244
+ const agentCount = hookConfig.agents?.length || 0;
245
+ const hookEnabled = hookConfig.enabled !== false;
246
+ const status = hookEnabled ? chalk_1.default.green('enabled') : chalk_1.default.dim('disabled');
247
+ console.log(chalk_1.default.dim(` - ${hookType}: ${status}, ${agentCount} agent(s)`));
248
+ }
249
+ }
250
+ }
251
+ }
252
+ else {
253
+ console.log(chalk_1.default.yellow('āš ļø Configuration: Not found'));
254
+ }
255
+ console.log();
256
+ }
257
+ /**
258
+ * Create the hooks command
259
+ */
260
+ function createHooksCommand() {
261
+ const hooksCommand = new commander_1.Command('hooks')
262
+ .description('Manage git hooks for running agents on code changes');
263
+ hooksCommand
264
+ .command('install')
265
+ .description('Install PRPM git hooks')
266
+ .action(handleInstall);
267
+ hooksCommand
268
+ .command('uninstall')
269
+ .description('Uninstall PRPM git hooks')
270
+ .action(handleUninstall);
271
+ hooksCommand
272
+ .command('run <hook-type>')
273
+ .description('Run agents for a specific hook (used internally by git)')
274
+ .action(handleRun);
275
+ hooksCommand
276
+ .command('status')
277
+ .description('Show status of installed hooks and configuration')
278
+ .action(handleStatus);
279
+ return hooksCommand;
280
+ }
@@ -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,76 @@
1
+ "use strict";
2
+ /**
3
+ * Remove command implementation
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleRemove = handleRemove;
7
+ exports.createRemoveCommand = createRemoveCommand;
8
+ const commander_1 = require("commander");
9
+ const lockfile_1 = require("../core/lockfile");
10
+ const filesystem_1 = require("../core/filesystem");
11
+ const fs_1 = require("fs");
12
+ /**
13
+ * Handle the remove command
14
+ */
15
+ async function handleRemove(name) {
16
+ try {
17
+ console.log(`šŸ—‘ļø Removing package: ${name}`);
18
+ // Remove from lockfile and get package info
19
+ const pkg = await (0, lockfile_1.removePackage)(name);
20
+ if (!pkg) {
21
+ console.error(`āŒ Package "${name}" not found`);
22
+ process.exit(1);
23
+ }
24
+ // Determine file path based on package type and format
25
+ const effectiveType = (pkg.format === 'claude' ? 'claude-skill' :
26
+ pkg.format === 'cursor' ? 'cursor' :
27
+ pkg.format === 'continue' ? 'continue' :
28
+ pkg.format === 'windsurf' ? 'windsurf' :
29
+ pkg.type);
30
+ const destDir = (0, filesystem_1.getDestinationDir)(effectiveType);
31
+ const fileExtension = pkg.format === 'cursor' ? 'mdc' : 'md';
32
+ // Strip author namespace to get just the package name
33
+ const packageName = (0, filesystem_1.stripAuthorNamespace)(name);
34
+ // Try single file first
35
+ const singleFilePath = `${destDir}/${packageName}.${fileExtension}`;
36
+ if (await (0, filesystem_1.fileExists)(singleFilePath)) {
37
+ // Single file package
38
+ await (0, filesystem_1.deleteFile)(singleFilePath);
39
+ console.log(` šŸ—‘ļø Deleted file: ${singleFilePath}`);
40
+ }
41
+ else {
42
+ // Try multi-file package directory
43
+ const packageDir = `${destDir}/${packageName}`;
44
+ try {
45
+ const stats = await fs_1.promises.stat(packageDir);
46
+ if (stats.isDirectory()) {
47
+ await fs_1.promises.rm(packageDir, { recursive: true, force: true });
48
+ console.log(` šŸ—‘ļø Deleted directory: ${packageDir}`);
49
+ }
50
+ }
51
+ catch (error) {
52
+ const err = error;
53
+ if (err.code !== 'ENOENT') {
54
+ console.warn(` āš ļø Could not delete package files: ${err.message}`);
55
+ }
56
+ }
57
+ }
58
+ console.log(`āœ… Successfully removed ${name}`);
59
+ process.exit(0);
60
+ }
61
+ catch (error) {
62
+ console.error(`āŒ Failed to remove package: ${error}`);
63
+ process.exit(1);
64
+ }
65
+ }
66
+ /**
67
+ * Create the remove command
68
+ */
69
+ function createRemoveCommand() {
70
+ const command = new commander_1.Command('remove');
71
+ command
72
+ .description('Remove a prompt package')
73
+ .argument('<id>', 'Package ID to remove')
74
+ .action(handleRemove);
75
+ return command;
76
+ }
@@ -0,0 +1,209 @@
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 execAsync = (0, util_1.promisify)(child_process_1.exec);
14
+ /**
15
+ * Make authenticated API call
16
+ */
17
+ async function apiCall(endpoint) {
18
+ const config = await (0, user_config_1.getConfig)();
19
+ const baseUrl = (config.registryUrl || "https://registry.prpm.dev").replace(/\/$/, '');
20
+ if (!config.token) {
21
+ throw new Error('Authentication required. Please run `prpm login` first.');
22
+ }
23
+ const response = await fetch(`${baseUrl}${endpoint}`, {
24
+ headers: {
25
+ Authorization: `Bearer ${config.token}`,
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ const errorData = await response.json().catch(() => ({}));
30
+ throw new Error(errorData.message || `API request failed: ${response.statusText}`);
31
+ }
32
+ return response;
33
+ }
34
+ /**
35
+ * Get current subscription status
36
+ */
37
+ async function getSubscriptionStatus() {
38
+ const response = await apiCall('/api/v1/playground/credits');
39
+ return response.json();
40
+ }
41
+ /**
42
+ * Open URL in default browser
43
+ */
44
+ async function openBrowser(url) {
45
+ const platform = process.platform;
46
+ let command;
47
+ if (platform === 'darwin') {
48
+ command = `open "${url}"`;
49
+ }
50
+ else if (platform === 'win32') {
51
+ command = `start "" "${url}"`;
52
+ }
53
+ else {
54
+ // Linux and other Unix-like systems
55
+ command = `xdg-open "${url}"`;
56
+ }
57
+ try {
58
+ await execAsync(command);
59
+ }
60
+ catch (error) {
61
+ // If automatic opening fails, just show the URL
62
+ console.log(`\nšŸ”— Please open this URL in your browser:`);
63
+ console.log(` ${url}`);
64
+ }
65
+ }
66
+ /**
67
+ * Poll for subscription status change
68
+ */
69
+ async function pollForSubscription(initialStatus, maxAttempts = 60, intervalMs = 2000) {
70
+ console.log('\nā³ Waiting for subscription confirmation...');
71
+ console.log(' (This may take a minute. Press Ctrl+C to cancel)');
72
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
73
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
74
+ try {
75
+ const status = await getSubscriptionStatus();
76
+ if (status.prpm_plus_status === 'active' && initialStatus !== 'active') {
77
+ return true;
78
+ }
79
+ // Show progress indicator
80
+ if (attempt % 5 === 0 && attempt > 0) {
81
+ process.stdout.write('.');
82
+ }
83
+ }
84
+ catch (error) {
85
+ // Continue polling even if there's an error
86
+ continue;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ /**
92
+ * Handle the subscribe command
93
+ */
94
+ async function handleSubscribe() {
95
+ const startTime = Date.now();
96
+ let success = false;
97
+ let error;
98
+ try {
99
+ const config = await (0, user_config_1.getConfig)();
100
+ if (!config.token) {
101
+ console.error('āŒ Authentication required');
102
+ console.log('\nšŸ’” Please login first:');
103
+ console.log(' prpm login');
104
+ process.exit(1);
105
+ }
106
+ // Get current status
107
+ console.log('šŸ” Checking current subscription status...');
108
+ const initialStatus = await getSubscriptionStatus();
109
+ if (initialStatus.prpm_plus_status === 'active') {
110
+ console.log('\nāœ… You are already subscribed to PRPM+!');
111
+ console.log(`\nšŸ“Š Current benefits:`);
112
+ console.log(` šŸ’° Monthly credits: ${initialStatus.monthly_credits}`);
113
+ console.log(` šŸ“¦ Priority support`);
114
+ console.log(` šŸš€ Early access to new features`);
115
+ console.log('\nšŸ’” Manage your subscription at:');
116
+ console.log(' https://prpm.dev/settings/billing');
117
+ process.exit(0);
118
+ }
119
+ console.log('\n✨ Subscribe to PRPM+ and get:');
120
+ console.log(' šŸ’° 100 monthly playground credits');
121
+ console.log(' ā™»ļø Rollover unused credits (up to 200)');
122
+ console.log(' šŸ“¦ Priority support');
123
+ console.log(' šŸš€ Early access to new features');
124
+ console.log('\nšŸ’µ Pricing:');
125
+ console.log(' $6/month for individuals');
126
+ console.log(' $3/month for verified organization members (50% off)');
127
+ // Open subscription page
128
+ const subscribeUrl = `${(config.registryUrl || "https://registry.prpm.dev").replace(/api\/?$/, '')}/playground/credits/subscribe`;
129
+ console.log(`\n🌐 Opening subscription page in your browser...`);
130
+ await openBrowser(subscribeUrl);
131
+ // Poll for subscription confirmation
132
+ const subscribed = await pollForSubscription(initialStatus.prpm_plus_status);
133
+ if (subscribed) {
134
+ const updatedStatus = await getSubscriptionStatus();
135
+ console.log('\n\nšŸŽ‰ Successfully subscribed to PRPM+!');
136
+ console.log('\nšŸ“Š Your benefits:');
137
+ console.log(` šŸ’° Monthly credits: ${updatedStatus.monthly_credits}`);
138
+ console.log(` šŸ’³ Current balance: ${updatedStatus.balance} credits`);
139
+ console.log('\nāœ… You can now:');
140
+ console.log(' - Test packages in playground: prpm playground <package> "<input>"');
141
+ console.log(' - Check credits anytime: prpm credits');
142
+ success = true;
143
+ }
144
+ else {
145
+ console.log('\n\nā±ļø Subscription process timed out or was canceled.');
146
+ console.log('\nšŸ’” If you completed the subscription, run this to verify:');
147
+ console.log(' prpm credits');
148
+ console.log('\nšŸ’” Or visit your account settings:');
149
+ console.log(' https://prpm.dev/settings/billing');
150
+ }
151
+ }
152
+ catch (err) {
153
+ error = err instanceof Error ? err.message : String(err);
154
+ console.error(`\nāŒ Subscription failed: ${error}`);
155
+ process.exit(1);
156
+ }
157
+ finally {
158
+ await telemetry_1.telemetry.track({
159
+ command: 'subscribe',
160
+ success,
161
+ error,
162
+ duration: Date.now() - startTime,
163
+ });
164
+ await telemetry_1.telemetry.shutdown();
165
+ }
166
+ }
167
+ /**
168
+ * Create the subscribe command
169
+ */
170
+ function createSubscribeCommand() {
171
+ const command = new commander_1.Command('subscribe');
172
+ command
173
+ .description('Subscribe to PRPM+ for monthly playground credits and benefits')
174
+ .addHelpText('after', `
175
+ PRPM+ Benefits:
176
+ ✨ 100 monthly playground credits (worth $6+ in API costs)
177
+ ā™»ļø Rollover unused credits up to 200 (1-month expiry)
178
+ šŸ“¦ Priority support and bug fixes
179
+ šŸš€ Early access to new features
180
+ šŸ’¬ Access to PRPM+ community
181
+
182
+ Pricing:
183
+ Individual: $6/month
184
+ Organization: $3/month (50% discount for verified org members)
185
+
186
+ How it works:
187
+ 1. Opens subscription page in your browser
188
+ 2. Complete payment with Stripe
189
+ 3. Credits are added automatically
190
+ 4. Start testing packages immediately
191
+
192
+ Examples:
193
+ # Subscribe to PRPM+
194
+ $ prpm subscribe
195
+
196
+ # After subscribing, check your credits
197
+ $ prpm credits
198
+
199
+ # Test packages in playground
200
+ $ prpm playground @anthropic/assistant "Help me brainstorm ideas"
201
+
202
+ Note: You can cancel anytime from https://prpm.dev/settings/billing
203
+ `)
204
+ .action(async () => {
205
+ await handleSubscribe();
206
+ process.exit(0);
207
+ });
208
+ return command;
209
+ }
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)());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Prompt Package Manager CLI - Install and manage prompt-based files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@octokit/rest": "^22.0.0",
48
48
  "@pr-pm/registry-client": "^1.3.4",
49
- "@pr-pm/types": "^0.2.4",
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",