prpm 1.2.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +90 -862
  2. package/dist/index.js +24163 -65
  3. package/dist/schemas/agents-md.schema.json +24 -0
  4. package/dist/schemas/aider.schema.json +24 -0
  5. package/dist/schemas/canonical.schema.json +435 -0
  6. package/dist/schemas/claude-agent.schema.json +62 -0
  7. package/dist/schemas/claude-hook.schema.json +70 -0
  8. package/dist/schemas/claude-plugin.schema.json +122 -0
  9. package/dist/schemas/claude-skill.schema.json +51 -0
  10. package/dist/schemas/claude-slash-command.schema.json +77 -0
  11. package/dist/schemas/claude.schema.json +52 -0
  12. package/dist/schemas/continue.schema.json +98 -0
  13. package/dist/schemas/copilot.schema.json +76 -0
  14. package/dist/schemas/cursor-command.schema.json +27 -0
  15. package/dist/schemas/cursor-hooks.schema.json +59 -0
  16. package/dist/schemas/cursor.schema.json +74 -0
  17. package/dist/schemas/droid-hook.schema.json +103 -0
  18. package/dist/schemas/droid-skill.schema.json +46 -0
  19. package/dist/schemas/droid-slash-command.schema.json +53 -0
  20. package/dist/schemas/droid.schema.json +46 -0
  21. package/dist/schemas/format-capabilities.schema.json +101 -0
  22. package/dist/schemas/format-registry.schema.json +99 -0
  23. package/dist/schemas/gemini-extension.schema.json +77 -0
  24. package/dist/schemas/gemini-md.schema.json +24 -0
  25. package/dist/schemas/gemini.schema.json +30 -0
  26. package/dist/schemas/kiro-agent.schema.json +146 -0
  27. package/dist/schemas/kiro-hook.schema.json +165 -0
  28. package/dist/schemas/kiro-steering.schema.json +74 -0
  29. package/dist/schemas/mcp-server.schema.json +130 -0
  30. package/dist/schemas/opencode-plugin.schema.json +391 -0
  31. package/dist/schemas/opencode-slash-command.schema.json +60 -0
  32. package/dist/schemas/opencode.schema.json +111 -0
  33. package/dist/schemas/prpm-manifest.schema.json +758 -0
  34. package/dist/schemas/replit.schema.json +21 -0
  35. package/dist/schemas/ruler.schema.json +22 -0
  36. package/dist/schemas/trae.schema.json +24 -0
  37. package/dist/schemas/windsurf.schema.json +22 -0
  38. package/dist/schemas/zed-extension.schema.json +238 -0
  39. package/dist/schemas/zed.schema.json +44 -0
  40. package/dist/schemas/zencoder.schema.json +51 -0
  41. package/package.json +20 -14
  42. package/schemas/prpm-manifest.schema.json +490 -39
  43. package/dist/__tests__/e2e/test-helpers.js +0 -150
  44. package/dist/commands/collections.js +0 -606
  45. package/dist/commands/index.js +0 -186
  46. package/dist/commands/info.js +0 -82
  47. package/dist/commands/install.js +0 -477
  48. package/dist/commands/list.js +0 -166
  49. package/dist/commands/login.js +0 -281
  50. package/dist/commands/outdated.js +0 -128
  51. package/dist/commands/popular.js +0 -27
  52. package/dist/commands/publish.js +0 -274
  53. package/dist/commands/schema.js +0 -37
  54. package/dist/commands/search.js +0 -404
  55. package/dist/commands/telemetry.js +0 -103
  56. package/dist/commands/trending.js +0 -76
  57. package/dist/commands/uninstall.js +0 -77
  58. package/dist/commands/update.js +0 -121
  59. package/dist/commands/upgrade.js +0 -121
  60. package/dist/commands/whoami.js +0 -75
  61. package/dist/core/claude-config.js +0 -91
  62. package/dist/core/cursor-config.js +0 -130
  63. package/dist/core/downloader.js +0 -64
  64. package/dist/core/filesystem.js +0 -124
  65. package/dist/core/lockfile.js +0 -239
  66. package/dist/core/marketplace-converter.js +0 -198
  67. package/dist/core/registry-client.js +0 -265
  68. package/dist/core/schema-validator.js +0 -74
  69. package/dist/core/telemetry.js +0 -175
  70. package/dist/core/user-config.js +0 -79
  71. package/dist/types/registry.js +0 -5
  72. package/dist/types.js +0 -5
@@ -1,121 +0,0 @@
1
- "use strict";
2
- /**
3
- * Update command - Update packages to latest compatible versions
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleUpdate = handleUpdate;
7
- exports.createUpdateCommand = createUpdateCommand;
8
- const commander_1 = require("commander");
9
- const registry_client_1 = require("@pr-pm/registry-client");
10
- const user_config_1 = require("../core/user-config");
11
- const lockfile_1 = require("../core/lockfile");
12
- const install_1 = require("./install");
13
- const telemetry_1 = require("../core/telemetry");
14
- /**
15
- * Update packages to latest minor/patch versions
16
- */
17
- async function handleUpdate(packageName, options = {}) {
18
- const startTime = Date.now();
19
- let success = false;
20
- let error;
21
- let updatedCount = 0;
22
- try {
23
- const config = await (0, user_config_1.getConfig)();
24
- const client = (0, registry_client_1.getRegistryClient)(config);
25
- const installedPackages = await (0, lockfile_1.listPackages)();
26
- if (installedPackages.length === 0) {
27
- console.log('No packages installed.');
28
- success = true;
29
- return;
30
- }
31
- // Determine which packages to update
32
- let packagesToUpdate = installedPackages;
33
- if (packageName) {
34
- // Update specific package
35
- packagesToUpdate = installedPackages.filter(p => p.id === packageName);
36
- if (packagesToUpdate.length === 0) {
37
- throw new Error(`Package ${packageName} is not installed`);
38
- }
39
- }
40
- console.log('🔄 Checking for updates...\n');
41
- for (const pkg of packagesToUpdate) {
42
- try {
43
- // Get package info from registry
44
- const registryPkg = await client.getPackage(pkg.id);
45
- if (!registryPkg.latest_version || !pkg.version) {
46
- continue;
47
- }
48
- const currentVersion = pkg.version;
49
- const latestVersion = registryPkg.latest_version.version;
50
- // Only update if it's a minor or patch update (not major)
51
- const updateType = getUpdateType(currentVersion, latestVersion);
52
- if (updateType === 'major') {
53
- console.log(`⏭️ Skipping ${pkg.id} (major update ${currentVersion} → ${latestVersion}, use upgrade)`);
54
- continue;
55
- }
56
- if (currentVersion === latestVersion) {
57
- console.log(`✅ ${pkg.id} is already up to date (${currentVersion})`);
58
- continue;
59
- }
60
- console.log(`\n📦 Updating ${pkg.id}: ${currentVersion} → ${latestVersion}`);
61
- // Install new version
62
- await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {
63
- type: pkg.type,
64
- });
65
- updatedCount++;
66
- }
67
- catch (err) {
68
- console.error(` ❌ Failed to update ${pkg.id}: ${err instanceof Error ? err.message : String(err)}`);
69
- }
70
- }
71
- if (updatedCount === 0) {
72
- console.log('\n✅ All packages are up to date!\n');
73
- }
74
- else {
75
- console.log(`\n✅ Updated ${updatedCount} package(s)\n`);
76
- }
77
- success = true;
78
- }
79
- catch (err) {
80
- error = err instanceof Error ? err.message : String(err);
81
- console.error(`\n❌ Update failed: ${error}`);
82
- process.exit(1);
83
- }
84
- finally {
85
- await telemetry_1.telemetry.track({
86
- command: 'update',
87
- success,
88
- error,
89
- duration: Date.now() - startTime,
90
- data: {
91
- packageName,
92
- updatedCount,
93
- },
94
- });
95
- await telemetry_1.telemetry.shutdown();
96
- }
97
- }
98
- /**
99
- * Determine update type based on semver
100
- */
101
- function getUpdateType(current, latest) {
102
- const currentParts = current.split('.').map(Number);
103
- const latestParts = latest.split('.').map(Number);
104
- const [currMajor = 0, currMinor = 0, currPatch = 0] = currentParts;
105
- const [latestMajor = 0, latestMinor = 0, latestPatch = 0] = latestParts;
106
- if (latestMajor > currMajor)
107
- return 'major';
108
- if (latestMinor > currMinor)
109
- return 'minor';
110
- return 'patch';
111
- }
112
- /**
113
- * Create the update command
114
- */
115
- function createUpdateCommand() {
116
- return new commander_1.Command('update')
117
- .description('Update packages to latest compatible versions (minor/patch only)')
118
- .argument('[package]', 'Specific package to update (optional)')
119
- .option('--all', 'Update all packages')
120
- .action(handleUpdate);
121
- }
@@ -1,121 +0,0 @@
1
- "use strict";
2
- /**
3
- * Upgrade command - Upgrade packages to latest versions (including major)
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleUpgrade = handleUpgrade;
7
- exports.createUpgradeCommand = createUpgradeCommand;
8
- const commander_1 = require("commander");
9
- const registry_client_1 = require("@pr-pm/registry-client");
10
- const user_config_1 = require("../core/user-config");
11
- const lockfile_1 = require("../core/lockfile");
12
- const install_1 = require("./install");
13
- const telemetry_1 = require("../core/telemetry");
14
- /**
15
- * Upgrade packages to latest versions (including major updates)
16
- */
17
- async function handleUpgrade(packageName, options = {}) {
18
- const startTime = Date.now();
19
- let success = false;
20
- let error;
21
- let upgradedCount = 0;
22
- try {
23
- const config = await (0, user_config_1.getConfig)();
24
- const client = (0, registry_client_1.getRegistryClient)(config);
25
- const installedPackages = await (0, lockfile_1.listPackages)();
26
- if (installedPackages.length === 0) {
27
- console.log('No packages installed.');
28
- success = true;
29
- return;
30
- }
31
- // Determine which packages to upgrade
32
- let packagesToUpgrade = installedPackages;
33
- if (packageName) {
34
- // Upgrade specific package
35
- packagesToUpgrade = installedPackages.filter(p => p.id === packageName);
36
- if (packagesToUpgrade.length === 0) {
37
- throw new Error(`Package ${packageName} is not installed`);
38
- }
39
- }
40
- console.log('🚀 Checking for upgrades...\n');
41
- for (const pkg of packagesToUpgrade) {
42
- try {
43
- // Get package info from registry
44
- const registryPkg = await client.getPackage(pkg.id);
45
- if (!registryPkg.latest_version || !pkg.version) {
46
- continue;
47
- }
48
- const currentVersion = pkg.version;
49
- const latestVersion = registryPkg.latest_version.version;
50
- if (currentVersion === latestVersion) {
51
- console.log(`✅ ${pkg.id} is already at latest version (${currentVersion})`);
52
- continue;
53
- }
54
- const updateType = getUpdateType(currentVersion, latestVersion);
55
- const emoji = updateType === 'major' ? '🔴' : updateType === 'minor' ? '🟡' : '🟢';
56
- console.log(`\n${emoji} Upgrading ${pkg.id}: ${currentVersion} → ${latestVersion} (${updateType})`);
57
- if (updateType === 'major' && !options.force) {
58
- console.log(` ⚠️ This is a major version upgrade and may contain breaking changes`);
59
- }
60
- // Install new version
61
- await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {
62
- type: pkg.type,
63
- });
64
- upgradedCount++;
65
- }
66
- catch (err) {
67
- console.error(` ❌ Failed to upgrade ${pkg.id}: ${err instanceof Error ? err.message : String(err)}`);
68
- }
69
- }
70
- if (upgradedCount === 0) {
71
- console.log('\n✅ All packages are at the latest version!\n');
72
- }
73
- else {
74
- console.log(`\n✅ Upgraded ${upgradedCount} package(s)\n`);
75
- }
76
- success = true;
77
- }
78
- catch (err) {
79
- error = err instanceof Error ? err.message : String(err);
80
- console.error(`\n❌ Upgrade failed: ${error}`);
81
- process.exit(1);
82
- }
83
- finally {
84
- await telemetry_1.telemetry.track({
85
- command: 'upgrade',
86
- success,
87
- error,
88
- duration: Date.now() - startTime,
89
- data: {
90
- packageName,
91
- upgradedCount,
92
- },
93
- });
94
- await telemetry_1.telemetry.shutdown();
95
- }
96
- }
97
- /**
98
- * Determine update type based on semver
99
- */
100
- function getUpdateType(current, latest) {
101
- const currentParts = current.split('.').map(Number);
102
- const latestParts = latest.split('.').map(Number);
103
- const [currMajor = 0, currMinor = 0, currPatch = 0] = currentParts;
104
- const [latestMajor = 0, latestMinor = 0, latestPatch = 0] = latestParts;
105
- if (latestMajor > currMajor)
106
- return 'major';
107
- if (latestMinor > currMinor)
108
- return 'minor';
109
- return 'patch';
110
- }
111
- /**
112
- * Create the upgrade command
113
- */
114
- function createUpgradeCommand() {
115
- return new commander_1.Command('upgrade')
116
- .description('Upgrade packages to latest versions (including major updates)')
117
- .argument('[package]', 'Specific package to upgrade (optional)')
118
- .option('--all', 'Upgrade all packages')
119
- .option('--force', 'Skip warning for major version upgrades')
120
- .action(handleUpgrade);
121
- }
@@ -1,75 +0,0 @@
1
- "use strict";
2
- /**
3
- * Whoami command implementation
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleWhoami = handleWhoami;
7
- exports.createWhoamiCommand = createWhoamiCommand;
8
- const commander_1 = require("commander");
9
- const user_config_1 = require("../core/user-config");
10
- const registry_client_1 = require("@pr-pm/registry-client");
11
- const telemetry_1 = require("../core/telemetry");
12
- /**
13
- * Show current logged-in user
14
- */
15
- async function handleWhoami() {
16
- const startTime = Date.now();
17
- let success = false;
18
- let error;
19
- try {
20
- const config = await (0, user_config_1.getConfig)();
21
- if (!config.token || !config.username) {
22
- console.log('Not logged in');
23
- console.log('\n💡 Run "prpm login" to authenticate\n');
24
- success = true;
25
- return;
26
- }
27
- // Fetch user profile from registry
28
- try {
29
- const client = (0, registry_client_1.getRegistryClient)(config);
30
- const userProfile = await client.getUserProfile(config.username);
31
- console.log(`\n👤 ${userProfile.username}${userProfile.verified_author ? ' ✓' : ''}`);
32
- if (userProfile.stats) {
33
- console.log(`\n📊 Stats:`);
34
- console.log(` 📦 Packages: ${userProfile.stats.total_packages}`);
35
- console.log(` ⬇️ Downloads: ${userProfile.stats.total_downloads.toLocaleString()}`);
36
- }
37
- // TODO: Add organizations when implemented in the database
38
- if (userProfile.organizations && userProfile.organizations.length > 0) {
39
- console.log(`\n🏢 Organizations:`);
40
- userProfile.organizations.forEach((org) => {
41
- console.log(` • ${org.name} (${org.role})`);
42
- });
43
- }
44
- console.log('');
45
- }
46
- catch (apiError) {
47
- // Fallback to simple username display if API call fails
48
- console.log(`${config.username}`);
49
- }
50
- success = true;
51
- }
52
- catch (err) {
53
- error = err instanceof Error ? err.message : String(err);
54
- console.error(`❌ Error: ${error}`);
55
- process.exit(1);
56
- }
57
- finally {
58
- // Track telemetry
59
- await telemetry_1.telemetry.track({
60
- command: 'whoami',
61
- success,
62
- error,
63
- duration: Date.now() - startTime,
64
- });
65
- await telemetry_1.telemetry.shutdown();
66
- }
67
- }
68
- /**
69
- * Create the whoami command
70
- */
71
- function createWhoamiCommand() {
72
- return new commander_1.Command('whoami')
73
- .description('Show current logged-in user')
74
- .action(handleWhoami);
75
- }
@@ -1,91 +0,0 @@
1
- "use strict";
2
- /**
3
- * Claude agent configuration utilities
4
- * Handles applying user config to Claude agent YAML frontmatter
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.hasClaudeHeader = hasClaudeHeader;
8
- exports.applyClaudeConfig = applyClaudeConfig;
9
- exports.parseClaudeFrontmatter = parseClaudeFrontmatter;
10
- /**
11
- * Check if content has Claude agent YAML frontmatter
12
- */
13
- function hasClaudeHeader(content) {
14
- return content.startsWith('---\n') && content.includes('name:');
15
- }
16
- /**
17
- * Apply user's Claude agent config to agent file
18
- * Merges user config with existing frontmatter, with user config taking precedence
19
- */
20
- function applyClaudeConfig(content, config) {
21
- if (!hasClaudeHeader(content)) {
22
- return content;
23
- }
24
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
25
- if (!match) {
26
- return content;
27
- }
28
- const [, frontmatterText, body] = match;
29
- // Parse existing frontmatter
30
- const frontmatter = {};
31
- frontmatterText.split('\n').forEach(line => {
32
- const colonIndex = line.indexOf(':');
33
- if (colonIndex > 0) {
34
- const key = line.substring(0, colonIndex).trim();
35
- const value = line.substring(colonIndex + 1).trim();
36
- frontmatter[key] = value;
37
- }
38
- });
39
- // Apply user config overrides
40
- if (config.tools !== undefined) {
41
- frontmatter.tools = config.tools;
42
- }
43
- if (config.model !== undefined) {
44
- frontmatter.model = config.model;
45
- }
46
- // Rebuild frontmatter
47
- const lines = ['---'];
48
- // Ensure required fields come first
49
- if (frontmatter.name) {
50
- lines.push(`name: ${frontmatter.name}`);
51
- }
52
- if (frontmatter.description) {
53
- lines.push(`description: ${frontmatter.description}`);
54
- }
55
- // Add optional fields
56
- const optionalFields = ['icon', 'tools', 'model'];
57
- for (const field of optionalFields) {
58
- if (frontmatter[field] && field !== 'name' && field !== 'description') {
59
- lines.push(`${field}: ${frontmatter[field]}`);
60
- }
61
- }
62
- // Add any other fields that might exist
63
- for (const [key, value] of Object.entries(frontmatter)) {
64
- if (!['name', 'description', 'icon', 'tools', 'model'].includes(key)) {
65
- lines.push(`${key}: ${value}`);
66
- }
67
- }
68
- lines.push('---');
69
- return lines.join('\n') + '\n' + body;
70
- }
71
- /**
72
- * Parse Claude agent frontmatter
73
- */
74
- function parseClaudeFrontmatter(content) {
75
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
76
- if (!match) {
77
- return { frontmatter: {}, body: content };
78
- }
79
- const [, frontmatterText, body] = match;
80
- // Simple YAML parsing (for basic key: value pairs)
81
- const frontmatter = {};
82
- frontmatterText.split('\n').forEach(line => {
83
- const colonIndex = line.indexOf(':');
84
- if (colonIndex > 0) {
85
- const key = line.substring(0, colonIndex).trim();
86
- const value = line.substring(colonIndex + 1).trim();
87
- frontmatter[key] = value;
88
- }
89
- });
90
- return { frontmatter, body };
91
- }
@@ -1,130 +0,0 @@
1
- "use strict";
2
- /**
3
- * Cursor MDC header configuration utilities
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.applyCursorConfig = applyCursorConfig;
7
- exports.hasMDCHeader = hasMDCHeader;
8
- exports.addMDCHeader = addMDCHeader;
9
- /**
10
- * Apply cursor config to MDC header in content
11
- * Replaces configurable fields in the YAML frontmatter
12
- */
13
- function applyCursorConfig(content, config) {
14
- // Check if content has MDC header (YAML frontmatter)
15
- if (!content.startsWith('---')) {
16
- return content;
17
- }
18
- const lines = content.split('\n');
19
- const headerEndIndex = lines.findIndex((line, index) => index > 0 && line === '---');
20
- if (headerEndIndex === -1) {
21
- // Malformed header, return as-is
22
- return content;
23
- }
24
- // Extract header lines (excluding the --- markers)
25
- const headerLines = lines.slice(1, headerEndIndex);
26
- const bodyLines = lines.slice(headerEndIndex + 1);
27
- // Parse and update header
28
- const updatedHeaderLines = [];
29
- let i = 0;
30
- while (i < headerLines.length) {
31
- const line = headerLines[i];
32
- // Check for fields that should be replaced by config
33
- if (line.startsWith('version:') && config.version) {
34
- updatedHeaderLines.push(`version: "${config.version}"`);
35
- i++;
36
- }
37
- else if (line.startsWith('globs:') && config.globs) {
38
- // Replace globs array
39
- updatedHeaderLines.push('globs:');
40
- config.globs.forEach((glob) => {
41
- updatedHeaderLines.push(` - "${glob}"`);
42
- });
43
- // Skip existing globs in the original header
44
- i++;
45
- while (i < headerLines.length && headerLines[i].startsWith(' - ')) {
46
- i++;
47
- }
48
- }
49
- else if (line.startsWith('alwaysApply:') && config.alwaysApply !== undefined) {
50
- updatedHeaderLines.push(`alwaysApply: ${config.alwaysApply}`);
51
- i++;
52
- }
53
- else if (line.startsWith('author:') && config.author) {
54
- // Replace existing author
55
- updatedHeaderLines.push(`author: "${config.author}"`);
56
- i++;
57
- }
58
- else if (line.startsWith('tags:') && config.tags) {
59
- // Replace tags array
60
- updatedHeaderLines.push('tags:');
61
- config.tags.forEach((tag) => {
62
- updatedHeaderLines.push(` - "${tag}"`);
63
- });
64
- // Skip existing tags in the original header
65
- i++;
66
- while (i < headerLines.length && headerLines[i].startsWith(' - ')) {
67
- i++;
68
- }
69
- }
70
- else {
71
- // Keep existing line
72
- updatedHeaderLines.push(line);
73
- i++;
74
- }
75
- }
76
- // Add new fields if they don't exist
77
- const hasAuthor = updatedHeaderLines.some(line => line.startsWith('author:'));
78
- const hasTags = updatedHeaderLines.some(line => line.startsWith('tags:'));
79
- if (config.author && !hasAuthor) {
80
- updatedHeaderLines.push(`author: "${config.author}"`);
81
- }
82
- if (config.tags && !hasTags) {
83
- updatedHeaderLines.push('tags:');
84
- config.tags.forEach((tag) => {
85
- updatedHeaderLines.push(` - "${tag}"`);
86
- });
87
- }
88
- // Reconstruct content
89
- return ['---', ...updatedHeaderLines, '---', ...bodyLines].join('\n');
90
- }
91
- /**
92
- * Check if content has valid MDC header (YAML frontmatter)
93
- * A valid MDC header must:
94
- * 1. Start with ---
95
- * 2. Have a closing --- on its own line
96
- * 3. Have at least a description field
97
- */
98
- function hasMDCHeader(content) {
99
- if (!content.startsWith('---\n')) {
100
- return false;
101
- }
102
- const lines = content.split('\n');
103
- const headerEndIndex = lines.findIndex((line, index) => index > 0 && line === '---');
104
- // Must have closing ---
105
- if (headerEndIndex === -1) {
106
- return false;
107
- }
108
- // Extract header lines (excluding the --- markers)
109
- const headerLines = lines.slice(1, headerEndIndex);
110
- // Must have at least one valid YAML field (typically description)
111
- const hasValidField = headerLines.some(line => {
112
- const trimmed = line.trim();
113
- return trimmed.length > 0 && trimmed.includes(':') && !trimmed.startsWith('#');
114
- });
115
- return hasValidField;
116
- }
117
- /**
118
- * Add MDC header to content if missing
119
- * Creates a basic YAML frontmatter with description
120
- */
121
- function addMDCHeader(content, packageDescription) {
122
- const description = packageDescription || 'Cursor rule for coding standards and best practices';
123
- const header = [
124
- '---',
125
- `description: "${description}"`,
126
- '---',
127
- '',
128
- ].join('\n');
129
- return header + content;
130
- }
@@ -1,64 +0,0 @@
1
- "use strict";
2
- /**
3
- * HTTP file downloading functionality
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.downloadFile = downloadFile;
7
- exports.extractFilename = extractFilename;
8
- // Use Node.js built-in fetch (available in Node 18+)
9
- /**
10
- * Download a file from a URL
11
- */
12
- async function downloadFile(url) {
13
- try {
14
- // Validate URL format
15
- if (!isValidUrl(url)) {
16
- throw new Error('Invalid URL format');
17
- }
18
- const response = await fetch(url);
19
- if (!response.ok) {
20
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
21
- }
22
- const content = await response.text();
23
- return content;
24
- }
25
- catch (error) {
26
- if (error instanceof Error) {
27
- throw new Error(`Failed to download file: ${error.message}`);
28
- }
29
- throw new Error('Failed to download file: Unknown error');
30
- }
31
- }
32
- /**
33
- * Validate if URL is a valid raw GitHub URL
34
- */
35
- function isValidUrl(url) {
36
- try {
37
- const urlObj = new URL(url);
38
- // For MVP, only support raw GitHub URLs
39
- return (urlObj.protocol === 'https:' &&
40
- (urlObj.hostname === 'raw.githubusercontent.com' ||
41
- urlObj.hostname === 'github.com' && urlObj.pathname.includes('/raw/')));
42
- }
43
- catch {
44
- return false;
45
- }
46
- }
47
- /**
48
- * Extract filename from URL
49
- */
50
- function extractFilename(url) {
51
- try {
52
- const urlObj = new URL(url);
53
- const pathname = urlObj.pathname;
54
- const filename = pathname.split('/').pop() || 'unknown';
55
- // If no extension, assume it's a markdown file
56
- if (!filename.includes('.')) {
57
- return `${filename}.md`;
58
- }
59
- return filename;
60
- }
61
- catch {
62
- return 'unknown.md';
63
- }
64
- }