reposer 1.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.
data/lib/repose/cli.rb ADDED
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "pastel"
5
+ require "tty-prompt"
6
+ require "tty-spinner"
7
+
8
+ module Repose
9
+ class CLI < Thor
10
+ def self.exit_on_failure?
11
+ true
12
+ end
13
+
14
+ desc "create [NAME]", "Create a new repository with AI assistance"
15
+ long_desc <<~DESC
16
+ Create a new GitHub repository with AI-generated description, topics,
17
+ and README. The AI will analyze the repository name and any additional
18
+ context you provide to generate appropriate content.
19
+
20
+ Examples:
21
+ $ repose create my-awesome-project
22
+ $ repose create web-scraper --language ruby --framework rails
23
+ DESC
24
+ option :language, type: :string, desc: "Primary programming language"
25
+ option :framework, type: :string, desc: "Framework or library to use"
26
+ option :description, type: :string, desc: "Custom description override"
27
+ option :private, type: :boolean, default: false, desc: "Create private repository"
28
+ option :template, type: :string, desc: "Repository template to use"
29
+ option :topics, type: :array, desc: "Custom topics/tags"
30
+ option :dry_run, type: :boolean, default: false, desc: "Preview without creating"
31
+ def create(name = nil)
32
+ pastel = Pastel.new
33
+ prompt = TTY::Prompt.new
34
+
35
+ puts pastel.cyan("🎯 Repose - AI Repository Creator")
36
+ puts pastel.dim("=" * 40)
37
+
38
+ # Get repository name
39
+ name ||= prompt.ask("Repository name:", required: true) do |q|
40
+ q.validate(/\A[a-zA-Z0-9._-]+\z/, "Invalid repository name format")
41
+ end
42
+
43
+ # Gather context
44
+ context = gather_context(name, options, prompt)
45
+
46
+ # Generate AI content
47
+ spinner = TTY::Spinner.new("[:spinner] Generating repository content with AI...", format: :dots)
48
+ spinner.auto_spin
49
+
50
+ begin
51
+ ai_content = AIGenerator.new.generate(context)
52
+ spinner.success("✅")
53
+ rescue => e
54
+ spinner.error("❌")
55
+ puts pastel.red("Error generating AI content: #{e.message}")
56
+ exit 1
57
+ end
58
+
59
+ # Display preview
60
+ display_preview(ai_content, pastel)
61
+
62
+ # Confirm creation
63
+ unless options[:dry_run]
64
+ if prompt.yes?("Create repository?")
65
+ create_repository(name, ai_content, options, pastel)
66
+ else
67
+ puts pastel.yellow("Repository creation cancelled.")
68
+ end
69
+ end
70
+ end
71
+
72
+ desc "configure", "Configure Repose settings"
73
+ def configure
74
+ prompt = TTY::Prompt.new
75
+ config = Repose.config
76
+
77
+ puts "🔧 Configuring Repose..."
78
+
79
+ github_token = prompt.mask("GitHub Personal Access Token:")
80
+ config.github_token = github_token unless github_token.empty?
81
+
82
+ openai_key = prompt.mask("OpenAI API Key:")
83
+ config.openai_api_key = openai_key unless openai_key.empty?
84
+
85
+ default_topics = prompt.ask("Default topics (comma-separated):")
86
+ config.default_topics = default_topics.split(",").map(&:strip) unless default_topics.empty?
87
+
88
+ config.save!
89
+ puts "✅ Configuration saved!"
90
+ end
91
+
92
+ desc "version", "Display version information"
93
+ def version
94
+ puts "Repose v#{Repose::VERSION}"
95
+ end
96
+
97
+ private
98
+
99
+ def gather_context(name, options, prompt)
100
+ context = {
101
+ name: name,
102
+ language: options[:language],
103
+ framework: options[:framework],
104
+ description: options[:description],
105
+ topics: options[:topics] || []
106
+ }
107
+
108
+ # Interactive prompts for missing context
109
+ unless context[:language]
110
+ languages = %w[ruby python javascript typescript java go rust php swift kotlin]
111
+ context[:language] = prompt.select("Primary programming language:", languages, per_page: 10)
112
+ end
113
+
114
+ unless context[:framework]
115
+ frameworks = framework_suggestions(context[:language])
116
+ if frameworks.any?
117
+ context[:framework] = prompt.select("Framework/Library (optional):", ["None"] + frameworks)
118
+ context[:framework] = nil if context[:framework] == "None"
119
+ end
120
+ end
121
+
122
+ # Additional context
123
+ context[:purpose] = prompt.ask("What will this project do? (optional):")
124
+
125
+ context
126
+ end
127
+
128
+ def framework_suggestions(language)
129
+ frameworks = {
130
+ "ruby" => ["Rails", "Sinatra", "Hanami", "Roda"],
131
+ "javascript" => ["React", "Vue", "Express", "Next.js", "Nuxt"],
132
+ "typescript" => ["React", "Vue", "Express", "Next.js", "Nuxt", "Angular"],
133
+ "python" => ["Django", "Flask", "FastAPI", "Streamlit"],
134
+ "java" => ["Spring Boot", "Quarkus", "Micronaut"],
135
+ "go" => ["Gin", "Echo", "Fiber", "Chi"],
136
+ "rust" => ["Actix", "Axum", "Warp", "Rocket"],
137
+ "swift" => ["Vapor", "Perfect", "Kitura"]
138
+ }
139
+
140
+ frameworks[language] || []
141
+ end
142
+
143
+ def display_preview(content, pastel)
144
+ puts "\n" + pastel.cyan("📋 Generated Repository Content")
145
+ puts pastel.dim("-" * 40)
146
+
147
+ puts pastel.bold("Name: ") + content[:name]
148
+ puts pastel.bold("Description: ") + content[:description]
149
+ puts pastel.bold("Topics: ") + content[:topics].join(", ")
150
+
151
+ puts "\n" + pastel.bold("README Preview:")
152
+ puts pastel.dim(content[:readme][0..300] + "...")
153
+ puts
154
+ end
155
+
156
+ def create_repository(name, content, options, pastel)
157
+ spinner = TTY::Spinner.new("[:spinner] Creating GitHub repository...", format: :dots)
158
+ spinner.auto_spin
159
+
160
+ begin
161
+ github_client = GitHubClient.new
162
+ repo = github_client.create_repository(
163
+ name: name,
164
+ description: content[:description],
165
+ private: options[:private],
166
+ topics: content[:topics],
167
+ readme: content[:readme]
168
+ )
169
+
170
+ spinner.success("✅")
171
+ puts pastel.green("Repository created successfully!")
172
+ puts pastel.cyan("🔗 #{repo.html_url}")
173
+ rescue => e
174
+ spinner.error("❌")
175
+ puts pastel.red("Error creating repository: #{e.message}")
176
+ exit 1
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Repose
6
+ class Config
7
+ attr_accessor :github_token, :openai_api_key, :default_topics, :default_language
8
+
9
+ def initialize
10
+ @default_topics = []
11
+ load_config
12
+ end
13
+
14
+ def config_file_path
15
+ File.expand_path("~/.repose.yml")
16
+ end
17
+
18
+ def load_config
19
+ return unless File.exist?(config_file_path)
20
+
21
+ config = YAML.load_file(config_file_path)
22
+ @github_token = config["github_token"]
23
+ @openai_api_key = config["openai_api_key"]
24
+ @default_topics = config["default_topics"] || []
25
+ @default_language = config["default_language"]
26
+ rescue => e
27
+ warn "Warning: Could not load config file: #{e.message}"
28
+ end
29
+
30
+ def save!
31
+ config_hash = {
32
+ "github_token" => @github_token,
33
+ "openai_api_key" => @openai_api_key,
34
+ "default_topics" => @default_topics,
35
+ "default_language" => @default_language
36
+ }.compact
37
+
38
+ File.write(config_file_path, YAML.dump(config_hash))
39
+ File.chmod(0600, config_file_path) # Secure the config file
40
+ end
41
+
42
+ def valid?
43
+ !@github_token.nil? && !@github_token.empty? &&
44
+ !@openai_api_key.nil? && !@openai_api_key.empty?
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repose
4
+ module Errors
5
+ class Error < StandardError; end
6
+ class ConfigError < Error; end
7
+ class GitHubError < Error; end
8
+ class AIError < Error; end
9
+ class ValidationError < Error; end
10
+ end
11
+
12
+ # Convenience aliases for AI providers
13
+ ConfigurationError = Errors::ConfigError
14
+ APIError = Errors::AIError
15
+ AuthenticationError = Errors::AIError
16
+ RateLimitError = Errors::AIError
17
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "octokit"
4
+
5
+ module Repose
6
+ class GitHubClient
7
+ def initialize
8
+ @client = Octokit::Client.new(access_token: Repose.config.github_token)
9
+ end
10
+
11
+ def create_repository(name:, description:, private: false, topics: [], readme: nil)
12
+ # Create the repository
13
+ repo = @client.create_repository(name, {
14
+ description: description,
15
+ private: private,
16
+ auto_init: false # We'll create our own README
17
+ })
18
+
19
+ # Add topics if provided
20
+ if topics.any?
21
+ @client.replace_all_topics(repo.full_name, topics.map(&:downcase))
22
+ end
23
+
24
+ # Create README if provided
25
+ if readme
26
+ @client.create_contents(
27
+ repo.full_name,
28
+ "README.md",
29
+ "Initial README",
30
+ readme,
31
+ branch: repo.default_branch
32
+ )
33
+ end
34
+
35
+ repo
36
+ rescue Octokit::Error => e
37
+ raise Errors::GitHubError, "GitHub API error: #{e.message}"
38
+ end
39
+
40
+ def repository_exists?(name)
41
+ @client.repository?("#{@client.user.login}/#{name}")
42
+ rescue Octokit::NotFound
43
+ false
44
+ end
45
+
46
+ def user_info
47
+ @client.user
48
+ rescue Octokit::Error => e
49
+ raise Errors::GitHubError, "Failed to fetch user info: #{e.message}"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repose
4
+ VERSION = "1.1.0"
5
+ end
data/lib/repose.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "repose/version"
4
+ require_relative "repose/cli"
5
+ require_relative "repose/github_client"
6
+ require_relative "repose/ai_generator"
7
+ require_relative "repose/config"
8
+ require_relative "repose/errors"
9
+
10
+ module Repose
11
+ class << self
12
+ def config
13
+ @config ||= Config.new
14
+ end
15
+
16
+ def configure
17
+ yield config if block_given?
18
+ end
19
+ end
20
+ end
data/recreate_repo.rb ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'net/http'
5
+ require 'json'
6
+ require 'uri'
7
+
8
+ def create_repo_with_personal_account
9
+ puts "🗑️ First, please manually delete the repository at:"
10
+ puts " https://github.com/svc-twm-crs/repose"
11
+ puts " Go to Settings > Danger Zone > Delete this repository"
12
+ puts ""
13
+
14
+ print "✅ Have you deleted the repository? (y/N): "
15
+ response = gets.chomp.downcase
16
+
17
+ unless response == 'y' || response == 'yes'
18
+ puts "❌ Please delete the repository first, then run this script again."
19
+ exit 1
20
+ end
21
+
22
+ puts ""
23
+ puts "📝 To create the repository under your personal account:"
24
+ puts "1. Go to: https://github.com/new"
25
+ puts "2. Repository name: repose"
26
+ puts "3. Description: AI-powered GitHub repository creation and management tool written in Ruby"
27
+ puts "4. Make it Public"
28
+ puts "5. Don't initialize with README (we have our own)"
29
+ puts "6. Click 'Create repository'"
30
+ puts ""
31
+
32
+ print "✅ Have you created the repository? (y/N): "
33
+ response = gets.chomp.downcase
34
+
35
+ unless response == 'y' || response == 'yes'
36
+ puts "❌ Please create the repository first, then run this script again."
37
+ exit 1
38
+ end
39
+
40
+ # Add the correct remote
41
+ puts ""
42
+ puts "🔗 Adding remote for wesleyscholl/repose..."
43
+
44
+ success = system("git remote add origin https://github.com/wesleyscholl/repose.git")
45
+
46
+ if success
47
+ puts "✅ Remote added successfully!"
48
+
49
+ puts "📤 Pushing to your repository..."
50
+ push_success = system("git push -u origin main")
51
+
52
+ if push_success
53
+ puts "🎉 Repository successfully created under your account!"
54
+ puts "🔗 https://github.com/wesleyscholl/repose"
55
+
56
+ puts ""
57
+ puts "🏷️ To add topics manually:"
58
+ puts "1. Go to: https://github.com/wesleyscholl/repose"
59
+ puts "2. Click the ⚙️ gear icon next to 'About'"
60
+ puts "3. Add these topics: ruby, cli, github, automation, ai, repository-management, thor, gem"
61
+ puts "4. Save changes"
62
+
63
+ else
64
+ puts "❌ Failed to push to repository"
65
+ end
66
+ else
67
+ puts "❌ Failed to add remote"
68
+ end
69
+ end
70
+
71
+ puts "🎯 Repose - Repository Recreation Script"
72
+ puts "=" * 50
73
+
74
+ create_repo_with_personal_account
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Simple script to create the GitHub repository for repose
5
+ # This demonstrates the kind of automation that repose will provide
6
+
7
+ require 'net/http'
8
+ require 'json'
9
+ require 'uri'
10
+
11
+ def create_github_repo
12
+ puts "🚀 Creating GitHub repository for Repose..."
13
+
14
+ # Check if gh CLI is available
15
+ if system('which gh > /dev/null 2>&1')
16
+ puts "✅ Found GitHub CLI, creating repository..."
17
+
18
+ repo_data = {
19
+ name: "repose",
20
+ description: "AI-powered GitHub repository creation and management tool written in Ruby",
21
+ topics: ["ruby", "cli", "github", "automation", "ai", "repository-management", "thor", "gem"],
22
+ private: false
23
+ }
24
+
25
+ # Create repository using GitHub CLI
26
+ cmd = [
27
+ "gh", "repo", "create", repo_data[:name],
28
+ "--description", repo_data[:description],
29
+ "--public",
30
+ "--source", ".",
31
+ "--remote", "origin",
32
+ "--push"
33
+ ]
34
+
35
+ success = system(*cmd)
36
+
37
+ if success
38
+ puts "✅ Repository created successfully!"
39
+ puts "🔗 Repository URL: https://github.com/wesleyscholl/repose"
40
+
41
+ # Add topics using GitHub API
42
+ puts "🏷️ Adding topics..."
43
+ topic_cmd = [
44
+ "gh", "api", "-X", "PUT",
45
+ "/repos/wesleyscholl/repose/topics",
46
+ "-f", "names=#{repo_data[:topics].join(',')}"
47
+ ]
48
+
49
+ if system(*topic_cmd)
50
+ puts "✅ Topics added successfully!"
51
+ else
52
+ puts "⚠️ Topics may not have been added, but repository was created"
53
+ end
54
+
55
+ puts "\n🎉 Next steps:"
56
+ puts "1. Visit: https://github.com/wesleyscholl/repose"
57
+ puts "2. Set up GitHub Actions for CI/CD"
58
+ puts "3. Configure RubyGems publishing"
59
+ puts "4. Add OpenAI integration when ready"
60
+
61
+ else
62
+ puts "❌ Failed to create repository"
63
+ exit 1
64
+ end
65
+ else
66
+ puts "❌ GitHub CLI not found. Please install it first:"
67
+ puts " brew install gh"
68
+ puts " gh auth login"
69
+ exit 1
70
+ end
71
+ end
72
+
73
+ def main
74
+ puts "🎯 Repose Repository Setup"
75
+ puts "=" * 40
76
+
77
+ # Check if we're in a git repository
78
+ unless Dir.exist?('.git')
79
+ puts "❌ Not in a git repository. Please run 'git init' first."
80
+ exit 1
81
+ end
82
+
83
+ # Check if there are commits
84
+ if `git rev-list --count HEAD 2>/dev/null`.strip == "0"
85
+ puts "❌ No commits found. Please make an initial commit first."
86
+ exit 1
87
+ end
88
+
89
+ create_github_repo
90
+ end
91
+
92
+ main if __FILE__ == $PROGRAM_NAME
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reposer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wesley Scholl
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: octokit
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '6.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '6.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: faraday
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: pastel
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.8'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.8'
68
+ - !ruby/object:Gem::Dependency
69
+ name: tty-prompt
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.23'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.23'
82
+ - !ruby/object:Gem::Dependency
83
+ name: tty-spinner
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.9'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.9'
96
+ - !ruby/object:Gem::Dependency
97
+ name: yaml
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.3'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.3'
110
+ - !ruby/object:Gem::Dependency
111
+ name: ostruct
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.6'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.6'
124
+ description: Reposer is an intelligent tool that uses AI to create GitHub repositories
125
+ with smart descriptions, topics, READMEs, and project structure
126
+ email:
127
+ - wesleyscholl@gmail.com
128
+ executables:
129
+ - reposer
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".rspec"
134
+ - ".rubocop.yml"
135
+ - ".ruby-version"
136
+ - CHANGELOG.md
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - LICENSE
140
+ - README.md
141
+ - READY_FOR_RELEASE.md
142
+ - RELEASE_GUIDE.md
143
+ - Rakefile
144
+ - demo.rb
145
+ - demo_ai_providers.rb
146
+ - demo_interactive.rb
147
+ - exe/repose
148
+ - exe/reposer
149
+ - lib/repose.rb
150
+ - lib/repose/ai/gemini_provider.rb
151
+ - lib/repose/ai/ollama_provider.rb
152
+ - lib/repose/ai_generator.rb
153
+ - lib/repose/cli.rb
154
+ - lib/repose/config.rb
155
+ - lib/repose/errors.rb
156
+ - lib/repose/github_client.rb
157
+ - lib/repose/version.rb
158
+ - recreate_repo.rb
159
+ - setup_github_repo.rb
160
+ homepage: https://github.com/wesleyscholl/repose
161
+ licenses:
162
+ - MIT
163
+ metadata:
164
+ allowed_push_host: https://rubygems.org
165
+ homepage_uri: https://github.com/wesleyscholl/repose
166
+ source_code_uri: https://github.com/wesleyscholl/repose
167
+ changelog_uri: https://github.com/wesleyscholl/repose/blob/main/CHANGELOG.md
168
+ rubygems_mfa_required: 'true'
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 3.0.0
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubygems_version: 3.7.2
184
+ specification_version: 4
185
+ summary: AI-powered GitHub repository creation and management
186
+ test_files: []