reposer 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +1 -1
- data/exe/repo-composer +6 -0
- data/lib/repose/ai/gemini_provider.rb +22 -16
- data/lib/repose/ai/ollama_provider.rb +21 -15
- data/lib/repose/ai_generator.rb +146 -14
- data/lib/repose/cli.rb +25 -3
- data/lib/repose/github_client.rb +55 -10
- data/lib/repose/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: acc16509f36a2ffb3c63ecbd7275d296c40f0e6b672991a34f795b7b4b3aeaf7
|
|
4
|
+
data.tar.gz: 2ddcc423700c0826b8068fb7d494b995b5df68b5f6dd5075c003dc661ef604d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4c9d3615aa806706139e6b15db908c69e5e901491d1fd935999cd3a27b48f38c0ffac969473bc09b99e517dd4e7b4209d8edd4a85b754fe3f7071c147d9b22c
|
|
7
|
+
data.tar.gz: a022e0a8861813f8bed4737e6c8e5d70567fa5ba23e7e22641fcb653dd67b703d2421767bac0576cf4e5570daf503926c06043b0399bdbb104a42607d7414fa5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.0] - 2025-11-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **20 Topics Generation**: AI now generates up to 20 relevant topics/tags instead of 5-8
|
|
14
|
+
- Enhanced topic generation for comprehensive repository tagging
|
|
15
|
+
- Includes language ecosystem, framework, architecture, deployment, and best practices topics
|
|
16
|
+
- Fallback template generates intelligent topics based on language, framework, and purpose
|
|
17
|
+
- **Emoji Support**: Automatic emoji inclusion for visual appeal
|
|
18
|
+
- Repository descriptions now include at least 2 relevant emojis
|
|
19
|
+
- README generation includes emojis in headers and sections
|
|
20
|
+
- Language-specific emojis (💎 Ruby, 🐍 Python, ⚡ JavaScript, etc.)
|
|
21
|
+
- Purpose-based emojis (🌐 API, 📊 Data, 🤖 AI/ML, etc.)
|
|
22
|
+
- **License Selection**: Interactive license type selection
|
|
23
|
+
- Support for MIT, Apache 2.0, GPL 3.0, BSD 3-Clause, MPL 2.0, Unlicense
|
|
24
|
+
- Custom/Other license option
|
|
25
|
+
- License passed through to GitHub repository creation
|
|
26
|
+
- README generation includes selected license
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- **GitHub Authentication**: Improved GitHub client token handling
|
|
30
|
+
- Now properly reads from `GITHUB_TOKEN` environment variable
|
|
31
|
+
- Better error messages for authentication failures
|
|
32
|
+
- Enhanced error handling for repository creation
|
|
33
|
+
- Support for GitHub API license templates
|
|
34
|
+
- **Topic Limits**: Removed artificial 8-topic limit, now supports up to 20
|
|
35
|
+
- **Context Propagation**: License now properly propagated through generation pipeline
|
|
36
|
+
|
|
37
|
+
### Enhanced
|
|
38
|
+
- **AI Providers**: Both Gemini and Ollama providers updated
|
|
39
|
+
- Better prompts for emoji and topic generation
|
|
40
|
+
- License-aware README generation
|
|
41
|
+
- Improved formatting and structure
|
|
42
|
+
- **Fallback Templates**: Enhanced template-based generation
|
|
43
|
+
- More intelligent topic selection based on project characteristics
|
|
44
|
+
- Language ecosystem topics (npm, bundler, cargo, etc.)
|
|
45
|
+
- Framework-related topics (web, api, microservices, etc.)
|
|
46
|
+
- Purpose-based topic detection (ai, data, testing, security, etc.)
|
|
47
|
+
|
|
10
48
|
## [1.1.0] - 2025-01-20
|
|
11
49
|
|
|
12
50
|
### Changed - Gem Renamed
|
data/Gemfile.lock
CHANGED
data/exe/repo-composer
ADDED
|
@@ -121,33 +121,38 @@ module Repose
|
|
|
121
121
|
|
|
122
122
|
def build_description_prompt(context)
|
|
123
123
|
<<~PROMPT
|
|
124
|
-
Generate a concise, professional GitHub repository description (max
|
|
124
|
+
Generate a concise, professional GitHub repository description (max 120 characters) for:
|
|
125
125
|
|
|
126
126
|
Repository name: #{context[:name]}
|
|
127
127
|
Language: #{context[:language]}
|
|
128
128
|
Framework: #{context[:framework]}
|
|
129
129
|
Purpose: #{context[:purpose]}
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
IMPORTANT: Include at least 2 relevant emojis that represent the project's purpose or technology.
|
|
132
|
+
Example format: "🚀 Fast API server for data processing 📊"
|
|
133
|
+
|
|
134
|
+
Return only the description text with emojis, no quotes or extra formatting.
|
|
132
135
|
PROMPT
|
|
133
136
|
end
|
|
134
137
|
|
|
135
138
|
def build_topics_prompt(context)
|
|
136
139
|
<<~PROMPT
|
|
137
|
-
Generate
|
|
140
|
+
Generate 20 relevant GitHub topics (keywords) for this repository:
|
|
138
141
|
|
|
139
142
|
Repository name: #{context[:name]}
|
|
140
143
|
Language: #{context[:language]}
|
|
141
144
|
Framework: #{context[:framework]}
|
|
142
145
|
Purpose: #{context[:purpose]}
|
|
143
146
|
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
Include topics for: language, framework, use-case, architecture, deployment, testing, best practices.
|
|
148
|
+
Return topics as comma-separated lowercase words (e.g., javascript, react, api, nodejs, docker, ci-cd).
|
|
149
|
+
No quotes, no explanations, just the comma-separated list of 20 topics.
|
|
146
150
|
PROMPT
|
|
147
151
|
end
|
|
148
152
|
|
|
149
153
|
def build_readme_prompt(context)
|
|
150
154
|
title = context[:name].split(/[-_]/).map(&:capitalize).join(" ")
|
|
155
|
+
license = context[:license] || "MIT"
|
|
151
156
|
|
|
152
157
|
<<~PROMPT
|
|
153
158
|
Generate a comprehensive README.md for a GitHub repository with these details:
|
|
@@ -156,17 +161,18 @@ module Repose
|
|
|
156
161
|
Language: #{context[:language]}
|
|
157
162
|
Framework: #{context[:framework]}
|
|
158
163
|
Purpose: #{context[:purpose]}
|
|
164
|
+
License: #{license}
|
|
159
165
|
|
|
160
|
-
Include these sections:
|
|
161
|
-
- Title and brief description
|
|
162
|
-
- Features (3-5 bullet points)
|
|
163
|
-
- Installation instructions (language-specific)
|
|
164
|
-
- Usage examples with code blocks
|
|
165
|
-
- Contributing guidelines
|
|
166
|
-
- License (
|
|
166
|
+
Include these sections with relevant emojis:
|
|
167
|
+
- Title with emoji and brief description with emojis
|
|
168
|
+
- ✨ Features section (3-5 bullet points with emojis)
|
|
169
|
+
- 🚀 Installation instructions (language-specific)
|
|
170
|
+
- 💻 Usage examples with code blocks
|
|
171
|
+
- 🤝 Contributing guidelines
|
|
172
|
+
- 📄 License (#{license})
|
|
167
173
|
|
|
168
|
-
Use proper Markdown formatting
|
|
169
|
-
Return only the README content, no extra commentary.
|
|
174
|
+
Use proper Markdown formatting with emojis throughout for visual appeal.
|
|
175
|
+
Be concise and professional. Return only the README content, no extra commentary.
|
|
170
176
|
PROMPT
|
|
171
177
|
end
|
|
172
178
|
|
|
@@ -185,8 +191,8 @@ module Repose
|
|
|
185
191
|
# Split by commas and clean up
|
|
186
192
|
topics = text.split(",").map(&:strip).map(&:downcase)
|
|
187
193
|
|
|
188
|
-
# Remove duplicates and limit to
|
|
189
|
-
topics.uniq.first(
|
|
194
|
+
# Remove duplicates and limit to 20
|
|
195
|
+
topics.uniq.first(20)
|
|
190
196
|
end
|
|
191
197
|
end
|
|
192
198
|
end
|
|
@@ -132,32 +132,37 @@ module Repose
|
|
|
132
132
|
|
|
133
133
|
def build_description_prompt(context)
|
|
134
134
|
<<~PROMPT
|
|
135
|
-
Generate a concise GitHub repository description (max
|
|
135
|
+
Generate a concise GitHub repository description (max 120 characters) for:
|
|
136
136
|
|
|
137
137
|
Repository: #{context[:name]}
|
|
138
138
|
Language: #{context[:language]}
|
|
139
139
|
Framework: #{context[:framework]}
|
|
140
140
|
Purpose: #{context[:purpose]}
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
IMPORTANT: Include at least 2 relevant emojis that represent the project.
|
|
143
|
+
Example: "🚀 Fast API server for data processing 📊"
|
|
144
|
+
|
|
145
|
+
Return ONLY the description text with emojis, no quotes or formatting.
|
|
143
146
|
PROMPT
|
|
144
147
|
end
|
|
145
148
|
|
|
146
149
|
def build_topics_prompt(context)
|
|
147
150
|
<<~PROMPT
|
|
148
|
-
Generate
|
|
151
|
+
Generate 20 GitHub topics for:
|
|
149
152
|
|
|
150
153
|
Repository: #{context[:name]}
|
|
151
154
|
Language: #{context[:language]}
|
|
152
155
|
Framework: #{context[:framework]}
|
|
153
156
|
Purpose: #{context[:purpose]}
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
Include topics for: language, framework, use-case, architecture, deployment, testing.
|
|
159
|
+
Return ONLY comma-separated lowercase keywords (e.g., python, api, docker, cli, testing, ci-cd).
|
|
156
160
|
PROMPT
|
|
157
161
|
end
|
|
158
162
|
|
|
159
163
|
def build_readme_prompt(context)
|
|
160
164
|
title = context[:name].split(/[-_]/).map(&:capitalize).join(" ")
|
|
165
|
+
license = context[:license] || "MIT"
|
|
161
166
|
|
|
162
167
|
<<~PROMPT
|
|
163
168
|
Create a GitHub README.md for:
|
|
@@ -166,17 +171,18 @@ module Repose
|
|
|
166
171
|
Language: #{context[:language]}
|
|
167
172
|
Framework: #{context[:framework]}
|
|
168
173
|
Purpose: #{context[:purpose]}
|
|
174
|
+
License: #{license}
|
|
169
175
|
|
|
170
|
-
Include:
|
|
171
|
-
- Title (# #{title})
|
|
172
|
-
- Brief description
|
|
173
|
-
- Features (3-5 bullet points)
|
|
174
|
-
- Installation (#{context[:language]}-specific commands)
|
|
175
|
-
- Usage with code examples
|
|
176
|
-
- Contributing
|
|
177
|
-
-
|
|
176
|
+
Include sections with emojis:
|
|
177
|
+
- Title with emoji (# 🚀 #{title})
|
|
178
|
+
- Brief description with emojis
|
|
179
|
+
- ✨ Features (3-5 bullet points with emojis)
|
|
180
|
+
- 🚀 Installation (#{context[:language]}-specific commands)
|
|
181
|
+
- 💻 Usage with code examples
|
|
182
|
+
- 🤝 Contributing
|
|
183
|
+
- 📄 License (#{license})
|
|
178
184
|
|
|
179
|
-
Use proper Markdown. Return ONLY the README content.
|
|
185
|
+
Use proper Markdown with emojis. Return ONLY the README content.
|
|
180
186
|
PROMPT
|
|
181
187
|
end
|
|
182
188
|
|
|
@@ -196,8 +202,8 @@ module Repose
|
|
|
196
202
|
# Extract comma-separated values
|
|
197
203
|
topics = text.split(",").map(&:strip).map(&:downcase)
|
|
198
204
|
|
|
199
|
-
# Remove duplicates, filter out empty, limit to
|
|
200
|
-
topics.reject(&:empty?).uniq.first(
|
|
205
|
+
# Remove duplicates, filter out empty, limit to 20
|
|
206
|
+
topics.reject(&:empty?).uniq.first(20)
|
|
201
207
|
end
|
|
202
208
|
end
|
|
203
209
|
end
|
data/lib/repose/ai_generator.rb
CHANGED
|
@@ -16,7 +16,8 @@ module Repose
|
|
|
16
16
|
name: context[:name],
|
|
17
17
|
description: generate_description(context),
|
|
18
18
|
topics: generate_topics(context),
|
|
19
|
-
readme: generate_readme(context)
|
|
19
|
+
readme: generate_readme(context),
|
|
20
|
+
license: context[:license]
|
|
20
21
|
}
|
|
21
22
|
end
|
|
22
23
|
|
|
@@ -93,40 +94,84 @@ module Repose
|
|
|
93
94
|
end
|
|
94
95
|
|
|
95
96
|
def generate_fallback_description(context)
|
|
96
|
-
# Fallback description generation without AI
|
|
97
|
-
|
|
97
|
+
# Fallback description generation without AI - with emojis
|
|
98
|
+
emoji = select_emoji_for_language(context[:language])
|
|
99
|
+
purpose_emoji = select_emoji_for_purpose(context[:purpose])
|
|
100
|
+
|
|
101
|
+
base_desc = "#{emoji} A #{context[:language]}"
|
|
98
102
|
base_desc += " #{context[:framework]}" if context[:framework]
|
|
99
103
|
base_desc += " project"
|
|
100
104
|
base_desc += " for #{context[:purpose]}" if context[:purpose] && !context[:purpose].empty?
|
|
105
|
+
base_desc += " #{purpose_emoji}" if purpose_emoji
|
|
101
106
|
|
|
102
107
|
base_desc.capitalize
|
|
103
108
|
end
|
|
104
109
|
|
|
105
110
|
def generate_fallback_topics(context)
|
|
106
|
-
#
|
|
111
|
+
# Enhanced topic generation without AI - generate up to 20 relevant topics
|
|
107
112
|
topics = []
|
|
108
113
|
topics << context[:language].downcase if context[:language]
|
|
109
114
|
topics << context[:framework].downcase if context[:framework]
|
|
110
115
|
|
|
111
|
-
#
|
|
116
|
+
# Language ecosystem topics
|
|
117
|
+
language_topics = language_ecosystem_topics(context[:language])
|
|
118
|
+
topics.concat(language_topics)
|
|
119
|
+
|
|
120
|
+
# Framework-specific topics
|
|
121
|
+
if context[:framework]
|
|
122
|
+
framework_topics = framework_related_topics(context[:framework])
|
|
123
|
+
topics.concat(framework_topics)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Add topics based on name patterns
|
|
112
127
|
name_lower = context[:name].downcase
|
|
113
128
|
topics << "api" if name_lower.include?("api")
|
|
114
|
-
topics << "
|
|
129
|
+
topics << "rest" if name_lower.include?("api") || name_lower.include?("rest")
|
|
130
|
+
topics << "graphql" if name_lower.include?("graphql")
|
|
131
|
+
topics << "web" if name_lower.include?("web") || context[:framework]&.downcase&.match?(/(rails|django|flask|express)/)
|
|
115
132
|
topics << "cli" if name_lower.include?("cli") || name_lower.include?("command")
|
|
116
133
|
topics << "tool" if name_lower.include?("tool") || name_lower.include?("util")
|
|
134
|
+
topics << "library" if name_lower.include?("lib")
|
|
135
|
+
topics << "microservice" if name_lower.include?("micro") || name_lower.include?("service")
|
|
136
|
+
topics << "automation" if name_lower.include?("auto") || name_lower.include?("script")
|
|
137
|
+
topics << "devops" if name_lower.include?("devops") || name_lower.include?("deploy")
|
|
138
|
+
topics << "docker" if name_lower.include?("docker") || name_lower.include?("container")
|
|
139
|
+
topics << "kubernetes" if name_lower.include?("k8s") || name_lower.include?("kube")
|
|
140
|
+
|
|
141
|
+
# Purpose-based topics
|
|
142
|
+
if context[:purpose]
|
|
143
|
+
purpose_lower = context[:purpose].downcase
|
|
144
|
+
topics << "ai" if purpose_lower.match?(/(ai|artificial|intelligence|ml|machine|learning)/)
|
|
145
|
+
topics << "data" if purpose_lower.match?(/(data|analytics|etl)/)
|
|
146
|
+
topics << "testing" if purpose_lower.match?(/(test|qa|quality)/)
|
|
147
|
+
topics << "monitoring" if purpose_lower.match?(/(monitor|observ|metric)/)
|
|
148
|
+
topics << "security" if purpose_lower.match?(/(secur|auth|encrypt)/)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# General best practice topics
|
|
152
|
+
topics.concat(["opensource", "development", "best-practices"])
|
|
117
153
|
|
|
118
|
-
topics.uniq.first(
|
|
154
|
+
topics.uniq.first(20)
|
|
119
155
|
end
|
|
120
156
|
|
|
121
157
|
def generate_fallback_readme(context)
|
|
122
158
|
title = context[:name].split(/[-_]/).map(&:capitalize).join(" ")
|
|
159
|
+
emoji = select_emoji_for_language(context[:language])
|
|
160
|
+
license = context[:license] || "MIT"
|
|
123
161
|
|
|
124
162
|
<<~README
|
|
125
|
-
# #{title}
|
|
163
|
+
# #{emoji} #{title}
|
|
126
164
|
|
|
127
|
-
A #{context[:language]} #{context[:framework] ? "#{context[:framework]} " : ""}project#{context[:purpose] && !context[:purpose].empty? ? " for #{context[:purpose]}" : ""}.
|
|
165
|
+
🚀 A #{context[:language]} #{context[:framework] ? "#{context[:framework]} " : ""}project#{context[:purpose] && !context[:purpose].empty? ? " for #{context[:purpose]}" : ""}.
|
|
128
166
|
|
|
129
|
-
##
|
|
167
|
+
## ✨ Features
|
|
168
|
+
|
|
169
|
+
- 🛠️ Modern #{context[:language]} development
|
|
170
|
+
#{context[:framework] ? "- 🏛️ Built with #{context[:framework]}" : ""}
|
|
171
|
+
- 📚 Comprehensive documentation
|
|
172
|
+
- ✅ Production-ready code
|
|
173
|
+
|
|
174
|
+
## 🚀 Installation
|
|
130
175
|
|
|
131
176
|
```bash
|
|
132
177
|
git clone https://github.com/yourusername/#{context[:name]}.git
|
|
@@ -135,20 +180,20 @@ module Repose
|
|
|
135
180
|
|
|
136
181
|
#{language_specific_install_instructions(context[:language])}
|
|
137
182
|
|
|
138
|
-
## Usage
|
|
183
|
+
## 💻 Usage
|
|
139
184
|
|
|
140
185
|
More documentation coming soon!
|
|
141
186
|
|
|
142
|
-
## Contributing
|
|
187
|
+
## 🤝 Contributing
|
|
143
188
|
|
|
144
189
|
1. Fork the repository
|
|
145
190
|
2. Create a feature branch
|
|
146
191
|
3. Make your changes
|
|
147
192
|
4. Submit a pull request
|
|
148
193
|
|
|
149
|
-
## License
|
|
194
|
+
## 📄 License
|
|
150
195
|
|
|
151
|
-
This project is licensed under the
|
|
196
|
+
This project is licensed under the #{license} License.
|
|
152
197
|
README
|
|
153
198
|
end
|
|
154
199
|
|
|
@@ -168,5 +213,92 @@ module Repose
|
|
|
168
213
|
""
|
|
169
214
|
end
|
|
170
215
|
end
|
|
216
|
+
|
|
217
|
+
def select_emoji_for_language(language)
|
|
218
|
+
emojis = {
|
|
219
|
+
"ruby" => "💎",
|
|
220
|
+
"python" => "🐍",
|
|
221
|
+
"javascript" => "⚡",
|
|
222
|
+
"typescript" => "📘",
|
|
223
|
+
"go" => "🚀",
|
|
224
|
+
"rust" => "🦀",
|
|
225
|
+
"java" => "☕",
|
|
226
|
+
"kotlin" => "🎯",
|
|
227
|
+
"swift" => "🍎",
|
|
228
|
+
"php" => "🐘",
|
|
229
|
+
"c" => "⚙️",
|
|
230
|
+
"c++" => "⚙️",
|
|
231
|
+
"c#" => "💠",
|
|
232
|
+
"scala" => "🎸",
|
|
233
|
+
"mojo" => "🔥"
|
|
234
|
+
}
|
|
235
|
+
emojis[language&.downcase] || "🚀"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def select_emoji_for_purpose(purpose)
|
|
239
|
+
return nil unless purpose && !purpose.empty?
|
|
240
|
+
|
|
241
|
+
purpose_lower = purpose.downcase
|
|
242
|
+
if purpose_lower.match?(/(api|rest|graphql)/)
|
|
243
|
+
"🌐"
|
|
244
|
+
elsif purpose_lower.match?(/(data|analytics|etl)/)
|
|
245
|
+
"📊"
|
|
246
|
+
elsif purpose_lower.match?(/(ai|ml|machine|learning)/)
|
|
247
|
+
"🤖"
|
|
248
|
+
elsif purpose_lower.match?(/(web|website|frontend)/)
|
|
249
|
+
"🎨"
|
|
250
|
+
elsif purpose_lower.match?(/(cli|command|terminal)/)
|
|
251
|
+
"💻"
|
|
252
|
+
elsif purpose_lower.match?(/(test|testing|qa)/)
|
|
253
|
+
"✅"
|
|
254
|
+
elsif purpose_lower.match?(/(deploy|devops|automation)/)
|
|
255
|
+
"⚙️"
|
|
256
|
+
elsif purpose_lower.match?(/(monitor|observ|metric)/)
|
|
257
|
+
"📈"
|
|
258
|
+
elsif purpose_lower.match?(/(secur|auth|encrypt)/)
|
|
259
|
+
"🔐"
|
|
260
|
+
elsif purpose_lower.match?(/(game|gaming)/)
|
|
261
|
+
"🎮"
|
|
262
|
+
elsif purpose_lower.match?(/(chat|message|communication)/)
|
|
263
|
+
"💬"
|
|
264
|
+
else
|
|
265
|
+
"✨"
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def language_ecosystem_topics(language)
|
|
270
|
+
topics_map = {
|
|
271
|
+
"ruby" => ["gem", "bundler", "rails", "ruby-on-rails"],
|
|
272
|
+
"python" => ["pip", "pypi", "django", "flask"],
|
|
273
|
+
"javascript" => ["npm", "nodejs", "webpack", "babel"],
|
|
274
|
+
"typescript" => ["npm", "nodejs", "webpack", "types"],
|
|
275
|
+
"go" => ["golang", "modules", "concurrent"],
|
|
276
|
+
"rust" => ["cargo", "crates", "systems-programming"],
|
|
277
|
+
"java" => ["maven", "gradle", "jvm", "spring"],
|
|
278
|
+
"kotlin" => ["gradle", "jvm", "android"],
|
|
279
|
+
"swift" => ["cocoapods", "spm", "ios"],
|
|
280
|
+
"php" => ["composer", "laravel", "symfony"],
|
|
281
|
+
"c#" => ["dotnet", "nuget", "asp-net"],
|
|
282
|
+
"scala" => ["sbt", "jvm", "functional"]
|
|
283
|
+
}
|
|
284
|
+
topics_map[language&.downcase] || []
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def framework_related_topics(framework)
|
|
288
|
+
framework_lower = framework&.downcase
|
|
289
|
+
topics = []
|
|
290
|
+
|
|
291
|
+
# Web frameworks
|
|
292
|
+
topics.concat(["web", "mvc", "backend"]) if framework_lower&.match?(/(rails|django|flask|express|spring)/)
|
|
293
|
+
topics.concat(["web", "frontend", "spa"]) if framework_lower&.match?(/(react|vue|angular)/)
|
|
294
|
+
|
|
295
|
+
# API frameworks
|
|
296
|
+
topics.concat(["api", "rest", "microservices"]) if framework_lower&.match?(/(fastapi|gin|echo|actix)/)
|
|
297
|
+
|
|
298
|
+
# Full-stack frameworks
|
|
299
|
+
topics.concat(["fullstack", "ssr"]) if framework_lower&.match?(/(next|nuxt)/)
|
|
300
|
+
|
|
301
|
+
topics
|
|
302
|
+
end
|
|
171
303
|
end
|
|
172
304
|
end
|
data/lib/repose/cli.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Repose
|
|
|
20
20
|
Examples:
|
|
21
21
|
$ repose create my-awesome-project
|
|
22
22
|
$ repose create web-scraper --language ruby --framework rails
|
|
23
|
+
$ repose create api-server --license apache-2.0
|
|
23
24
|
DESC
|
|
24
25
|
option :language, type: :string, desc: "Primary programming language"
|
|
25
26
|
option :framework, type: :string, desc: "Framework or library to use"
|
|
@@ -27,6 +28,7 @@ module Repose
|
|
|
27
28
|
option :private, type: :boolean, default: false, desc: "Create private repository"
|
|
28
29
|
option :template, type: :string, desc: "Repository template to use"
|
|
29
30
|
option :topics, type: :array, desc: "Custom topics/tags"
|
|
31
|
+
option :license, type: :string, desc: "License type (mit, apache-2.0, gpl-3.0, bsd-3-clause, unlicense, etc.)"
|
|
30
32
|
option :dry_run, type: :boolean, default: false, desc: "Preview without creating"
|
|
31
33
|
def create(name = nil)
|
|
32
34
|
pastel = Pastel.new
|
|
@@ -102,12 +104,13 @@ module Repose
|
|
|
102
104
|
language: options[:language],
|
|
103
105
|
framework: options[:framework],
|
|
104
106
|
description: options[:description],
|
|
105
|
-
topics: options[:topics] || []
|
|
107
|
+
topics: options[:topics] || [],
|
|
108
|
+
license: options[:license]
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
# Interactive prompts for missing context
|
|
109
112
|
unless context[:language]
|
|
110
|
-
languages = %w[c c++ c# go java javascript kotlin mojo php python ruby rust scala typescript]
|
|
113
|
+
languages = %w[c c++ c# go java javascript kotlin mojo php python ruby rust scala swift typescript]
|
|
111
114
|
context[:language] = prompt.select("Primary programming language:", languages, per_page: 14)
|
|
112
115
|
end
|
|
113
116
|
|
|
@@ -119,6 +122,24 @@ module Repose
|
|
|
119
122
|
end
|
|
120
123
|
end
|
|
121
124
|
|
|
125
|
+
# License selection
|
|
126
|
+
unless context[:license]
|
|
127
|
+
licenses = [
|
|
128
|
+
{ name: "MIT License (Permissive, most popular)", value: "mit" },
|
|
129
|
+
{ name: "Apache 2.0 (Permissive with patent grant)", value: "apache-2.0" },
|
|
130
|
+
{ name: "GPL 3.0 (Copyleft, strong)", value: "gpl-3.0" },
|
|
131
|
+
{ name: "BSD 3-Clause (Permissive)", value: "bsd-3-clause" },
|
|
132
|
+
{ name: "Mozilla Public License 2.0", value: "mpl-2.0" },
|
|
133
|
+
{ name: "Unlicense (Public Domain)", value: "unlicense" },
|
|
134
|
+
{ name: "Other/Custom", value: "other" }
|
|
135
|
+
]
|
|
136
|
+
context[:license] = prompt.select("Choose a license:", licenses)
|
|
137
|
+
|
|
138
|
+
if context[:license] == "other"
|
|
139
|
+
context[:license] = prompt.ask("Enter license name:", default: "MIT")
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
122
143
|
# Additional context
|
|
123
144
|
context[:purpose] = prompt.ask("What will this project do? (optional):")
|
|
124
145
|
|
|
@@ -164,7 +185,8 @@ module Repose
|
|
|
164
185
|
description: content[:description],
|
|
165
186
|
private: options[:private],
|
|
166
187
|
topics: content[:topics],
|
|
167
|
-
readme: content[:readme]
|
|
188
|
+
readme: content[:readme],
|
|
189
|
+
license: content[:license]
|
|
168
190
|
)
|
|
169
191
|
|
|
170
192
|
spinner.success("✅")
|
data/lib/repose/github_client.rb
CHANGED
|
@@ -5,20 +5,35 @@ require "octokit"
|
|
|
5
5
|
module Repose
|
|
6
6
|
class GitHubClient
|
|
7
7
|
def initialize
|
|
8
|
-
|
|
8
|
+
token = Repose.config.github_token || ENV["GITHUB_TOKEN"]
|
|
9
|
+
|
|
10
|
+
raise Errors::ConfigurationError, "GitHub token not configured. Set GITHUB_TOKEN environment variable or run 'repose configure'" if token.nil? || token.empty?
|
|
11
|
+
|
|
12
|
+
@client = Octokit::Client.new(access_token: token)
|
|
13
|
+
@client.auto_paginate = true
|
|
9
14
|
end
|
|
10
15
|
|
|
11
|
-
def create_repository(name:, description:, private: false, topics: [], readme: nil)
|
|
12
|
-
# Create the repository
|
|
13
|
-
|
|
16
|
+
def create_repository(name:, description:, private: false, topics: [], readme: nil, license: nil)
|
|
17
|
+
# Create the repository with license template if specified
|
|
18
|
+
repo_options = {
|
|
14
19
|
description: description,
|
|
15
20
|
private: private,
|
|
16
|
-
auto_init: false # We'll create our own README
|
|
17
|
-
|
|
21
|
+
auto_init: false, # We'll create our own README
|
|
22
|
+
has_issues: true,
|
|
23
|
+
has_wiki: true,
|
|
24
|
+
has_projects: true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Add license template if specified
|
|
28
|
+
if license && !license.empty?
|
|
29
|
+
repo_options[:license_template] = normalize_license_key(license)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
repo = @client.create_repository(name, repo_options)
|
|
18
33
|
|
|
19
34
|
# Add topics if provided
|
|
20
35
|
if topics.any?
|
|
21
|
-
@client.replace_all_topics(repo.full_name, topics.map(&:downcase))
|
|
36
|
+
@client.replace_all_topics(repo.full_name, topics.map(&:downcase).uniq)
|
|
22
37
|
end
|
|
23
38
|
|
|
24
39
|
# Create README if provided
|
|
@@ -26,27 +41,57 @@ module Repose
|
|
|
26
41
|
@client.create_contents(
|
|
27
42
|
repo.full_name,
|
|
28
43
|
"README.md",
|
|
29
|
-
"Initial README",
|
|
44
|
+
"Initial commit: Add README",
|
|
30
45
|
readme,
|
|
31
|
-
branch: repo.default_branch
|
|
46
|
+
branch: repo.default_branch || "main"
|
|
32
47
|
)
|
|
33
48
|
end
|
|
34
49
|
|
|
35
50
|
repo
|
|
51
|
+
rescue Octokit::Unauthorized => e
|
|
52
|
+
raise Errors::AuthenticationError, "GitHub authentication failed. Check your token permissions: #{e.message}"
|
|
53
|
+
rescue Octokit::UnprocessableEntity => e
|
|
54
|
+
raise Errors::GitHubError, "Repository creation failed (repository may already exist): #{e.message}"
|
|
36
55
|
rescue Octokit::Error => e
|
|
37
56
|
raise Errors::GitHubError, "GitHub API error: #{e.message}"
|
|
38
57
|
end
|
|
39
58
|
|
|
40
59
|
def repository_exists?(name)
|
|
41
|
-
@client.
|
|
60
|
+
username = @client.user.login
|
|
61
|
+
@client.repository?("#{username}/#{name}")
|
|
42
62
|
rescue Octokit::NotFound
|
|
43
63
|
false
|
|
64
|
+
rescue Octokit::Unauthorized => e
|
|
65
|
+
raise Errors::AuthenticationError, "GitHub authentication failed: #{e.message}"
|
|
44
66
|
end
|
|
45
67
|
|
|
46
68
|
def user_info
|
|
47
69
|
@client.user
|
|
70
|
+
rescue Octokit::Unauthorized => e
|
|
71
|
+
raise Errors::AuthenticationError, "Failed to authenticate with GitHub. Check your token: #{e.message}"
|
|
48
72
|
rescue Octokit::Error => e
|
|
49
73
|
raise Errors::GitHubError, "Failed to fetch user info: #{e.message}"
|
|
50
74
|
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def normalize_license_key(license)
|
|
79
|
+
# GitHub API uses specific license keys
|
|
80
|
+
license_map = {
|
|
81
|
+
"mit" => "mit",
|
|
82
|
+
"apache" => "apache-2.0",
|
|
83
|
+
"apache-2.0" => "apache-2.0",
|
|
84
|
+
"gpl" => "gpl-3.0",
|
|
85
|
+
"gpl-3.0" => "gpl-3.0",
|
|
86
|
+
"bsd" => "bsd-3-clause",
|
|
87
|
+
"bsd-3-clause" => "bsd-3-clause",
|
|
88
|
+
"mpl" => "mpl-2.0",
|
|
89
|
+
"mpl-2.0" => "mpl-2.0",
|
|
90
|
+
"unlicense" => "unlicense"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
normalized = license_map[license.downcase] || license.downcase
|
|
94
|
+
normalized
|
|
95
|
+
end
|
|
51
96
|
end
|
|
52
97
|
end
|
data/lib/repose/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reposer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wesley Scholl
|
|
@@ -144,6 +144,7 @@ files:
|
|
|
144
144
|
- demo.rb
|
|
145
145
|
- demo_ai_providers.rb
|
|
146
146
|
- demo_interactive.rb
|
|
147
|
+
- exe/repo-composer
|
|
147
148
|
- exe/repose
|
|
148
149
|
- exe/reposer
|
|
149
150
|
- lib/repose.rb
|