ruby-ai-gem-context 0.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +81 -0
- data/LICENSE.txt +21 -0
- data/README.md +236 -0
- data/Rakefile +59 -0
- data/lib/ruby_ai_gem_context/config_file.rb +97 -0
- data/lib/ruby_ai_gem_context/configuration.rb +61 -0
- data/lib/ruby_ai_gem_context/file_collector.rb +110 -0
- data/lib/ruby_ai_gem_context/generator.rb +89 -0
- data/lib/ruby_ai_gem_context/interactive.rb +192 -0
- data/lib/ruby_ai_gem_context/platform.rb +79 -0
- data/lib/ruby_ai_gem_context/platforms/claude.rb +108 -0
- data/lib/ruby_ai_gem_context/platforms/codex.rb +130 -0
- data/lib/ruby_ai_gem_context/platforms/cursor.rb +88 -0
- data/lib/ruby_ai_gem_context/platforms/llm_txt.rb +90 -0
- data/lib/ruby_ai_gem_context/platforms/windsurf.rb +82 -0
- data/lib/ruby_ai_gem_context/railtie.rb +24 -0
- data/lib/ruby_ai_gem_context/tasks/ruby_ai_gem_context.rake +311 -0
- data/lib/ruby_ai_gem_context/version.rb +5 -0
- data/lib/ruby_ai_gem_context.rb +60 -0
- data/ruby-ai-gem-context.gemspec +41 -0
- metadata +150 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_llm"
|
|
4
|
+
|
|
5
|
+
module RubyAiGemContext
|
|
6
|
+
# Generates context files using RubyLLM
|
|
7
|
+
class Generator
|
|
8
|
+
attr_reader :platform, :file_collector, :options
|
|
9
|
+
|
|
10
|
+
# @param platform [Class] Platform class (e.g., Platforms::Claude)
|
|
11
|
+
# @param file_collector [FileCollector] Collector with project files
|
|
12
|
+
# @param options [Hash] Additional options
|
|
13
|
+
def initialize(platform:, file_collector:, **options)
|
|
14
|
+
@platform = platform
|
|
15
|
+
@file_collector = file_collector
|
|
16
|
+
@options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Generate content for the platform
|
|
20
|
+
# @return [String] Generated content
|
|
21
|
+
def generate
|
|
22
|
+
files = file_collector.collect
|
|
23
|
+
prompt = build_prompt(files)
|
|
24
|
+
|
|
25
|
+
response = chat(prompt)
|
|
26
|
+
response.content
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Build the full prompt including project context
|
|
30
|
+
def build_prompt(files)
|
|
31
|
+
<<~PROMPT
|
|
32
|
+
#{platform.generation_prompt}
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
Here is the project's source code and files for context:
|
|
37
|
+
|
|
38
|
+
#{format_files(files)}
|
|
39
|
+
PROMPT
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def chat(prompt)
|
|
45
|
+
model = options[:model] || RubyAiGemContext.configuration.model
|
|
46
|
+
|
|
47
|
+
RubyLLM.chat(
|
|
48
|
+
model: model,
|
|
49
|
+
messages: [{ role: "user", content: prompt }],
|
|
50
|
+
temperature: RubyAiGemContext.configuration.temperature,
|
|
51
|
+
max_tokens: RubyAiGemContext.configuration.max_tokens
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def format_files(files)
|
|
56
|
+
files.map do |path, content|
|
|
57
|
+
<<~FILE
|
|
58
|
+
### #{path}
|
|
59
|
+
|
|
60
|
+
```#{language_for(path)}
|
|
61
|
+
#{truncate_content(content)}
|
|
62
|
+
```
|
|
63
|
+
FILE
|
|
64
|
+
end.join("\n")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def language_for(path)
|
|
68
|
+
case File.extname(path)
|
|
69
|
+
when ".rb" then "ruby"
|
|
70
|
+
when ".rake" then "ruby"
|
|
71
|
+
when ".gemspec" then "ruby"
|
|
72
|
+
when ".yml", ".yaml" then "yaml"
|
|
73
|
+
when ".json" then "json"
|
|
74
|
+
when ".md" then "markdown"
|
|
75
|
+
when ".txt" then "text"
|
|
76
|
+
else ""
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def truncate_content(content, max_lines: 200)
|
|
81
|
+
lines = content.lines
|
|
82
|
+
if lines.size > max_lines
|
|
83
|
+
lines.first(max_lines).join + "\n# ... (truncated, #{lines.size - max_lines} more lines)"
|
|
84
|
+
else
|
|
85
|
+
content
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-prompt"
|
|
4
|
+
require "tty-spinner"
|
|
5
|
+
|
|
6
|
+
module RubyAiGemContext
|
|
7
|
+
# Interactive terminal UI for rake tasks
|
|
8
|
+
class Interactive
|
|
9
|
+
attr_reader :prompt, :project_root
|
|
10
|
+
|
|
11
|
+
def initialize(project_root = Dir.pwd)
|
|
12
|
+
@prompt = TTY::Prompt.new
|
|
13
|
+
@project_root = project_root
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# === Setup Task ===
|
|
17
|
+
|
|
18
|
+
# Ask which platforms to generate boilerplate for
|
|
19
|
+
def select_platforms
|
|
20
|
+
choices = RubyAiGemContext::PLATFORMS.map do |key, platform_class|
|
|
21
|
+
{ name: "#{platform_class.name} (#{platform_class.filename})", value: key }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
prompt.multi_select(
|
|
25
|
+
"Which platforms do you want to generate context files for?",
|
|
26
|
+
choices,
|
|
27
|
+
help: "(Use space to select, enter to confirm)",
|
|
28
|
+
min: 1
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Handle collision when file exists
|
|
33
|
+
def handle_collision(platform_class)
|
|
34
|
+
prompt.select(
|
|
35
|
+
"#{platform_class.filename} already exists. What would you like to do?",
|
|
36
|
+
[
|
|
37
|
+
{ name: "Skip (keep existing)", value: :skip },
|
|
38
|
+
{ name: "Backup & overwrite (saves #{platform_class.filename}.backup)", value: :backup },
|
|
39
|
+
{ name: "Show diff (compare before deciding)", value: :diff },
|
|
40
|
+
{ name: "Overwrite (replace existing)", value: :overwrite }
|
|
41
|
+
]
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Show diff between existing and new content
|
|
46
|
+
def show_diff(existing, new_content)
|
|
47
|
+
puts "\n--- Existing content ---"
|
|
48
|
+
puts existing.lines.first(20).join
|
|
49
|
+
puts "..." if existing.lines.size > 20
|
|
50
|
+
puts "\n--- New content ---"
|
|
51
|
+
puts new_content.lines.first(20).join
|
|
52
|
+
puts "..." if new_content.lines.size > 20
|
|
53
|
+
puts
|
|
54
|
+
|
|
55
|
+
prompt.select("What would you like to do?", [
|
|
56
|
+
{ name: "Keep existing", value: :skip },
|
|
57
|
+
{ name: "Use new content", value: :overwrite },
|
|
58
|
+
{ name: "Backup & use new", value: :backup }
|
|
59
|
+
])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# === Generate Task ===
|
|
63
|
+
|
|
64
|
+
# Ask about source folders
|
|
65
|
+
def select_source_folders
|
|
66
|
+
choice = prompt.select(
|
|
67
|
+
"Where should we read source files from?",
|
|
68
|
+
[
|
|
69
|
+
{ name: "Project root (scan everything)", value: :root },
|
|
70
|
+
{ name: "Specific folders (you'll provide a list)", value: :specific }
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
case choice
|
|
75
|
+
when :root
|
|
76
|
+
nil # nil means scan from root
|
|
77
|
+
when :specific
|
|
78
|
+
ask_folder_list
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Ask for specific folder list
|
|
83
|
+
def ask_folder_list
|
|
84
|
+
input = prompt.ask(
|
|
85
|
+
"Enter folder paths (comma-separated, relative to project root):",
|
|
86
|
+
default: "lib, app"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
input.split(",").map(&:strip).reject(&:empty?)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Confirm source folders and show what will be scanned
|
|
93
|
+
def confirm_source_folders(folders, summary)
|
|
94
|
+
puts
|
|
95
|
+
puts "Will scan #{summary[:total_files]} files (#{format_size(summary[:total_size])})"
|
|
96
|
+
puts "File types: #{summary[:by_extension].map { |ext, count| "#{ext || 'no ext'}: #{count}" }.join(', ')}"
|
|
97
|
+
puts
|
|
98
|
+
|
|
99
|
+
prompt.yes?("Proceed with generation?")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Select which existing platforms to regenerate
|
|
103
|
+
def select_platforms_to_regenerate(existing_platforms)
|
|
104
|
+
choices = existing_platforms.map do |key|
|
|
105
|
+
platform_class = RubyAiGemContext.platform(key)
|
|
106
|
+
{ name: "#{platform_class.name} (#{platform_class.filename})", value: key }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
prompt.multi_select(
|
|
110
|
+
"Which context files do you want to regenerate?",
|
|
111
|
+
choices,
|
|
112
|
+
help: "(Use space to select, enter to confirm)"
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# === Generate for Gem Task ===
|
|
117
|
+
|
|
118
|
+
# Ask for gem name
|
|
119
|
+
def ask_gem_name
|
|
120
|
+
prompt.ask("Enter the gem name:") do |q|
|
|
121
|
+
q.required true
|
|
122
|
+
q.validate(/\A[\w-]+\z/, "Invalid gem name format")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# === Progress Indicators ===
|
|
127
|
+
|
|
128
|
+
# Show spinner while doing work
|
|
129
|
+
def with_spinner(message)
|
|
130
|
+
spinner = TTY::Spinner.new("[:spinner] #{message}", format: :dots)
|
|
131
|
+
spinner.auto_spin
|
|
132
|
+
result = yield
|
|
133
|
+
spinner.success("Done!")
|
|
134
|
+
result
|
|
135
|
+
rescue StandardError => e
|
|
136
|
+
spinner.error("Failed!")
|
|
137
|
+
raise e
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# === Output Helpers ===
|
|
141
|
+
|
|
142
|
+
def success(message)
|
|
143
|
+
puts prompt.decorate("✓ #{message}", :green)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def warning(message)
|
|
147
|
+
puts prompt.decorate("⚠ #{message}", :yellow)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def error(message)
|
|
151
|
+
puts prompt.decorate("✗ #{message}", :red)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def info(message)
|
|
155
|
+
puts prompt.decorate("ℹ #{message}", :cyan)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def header(message)
|
|
159
|
+
puts
|
|
160
|
+
puts prompt.decorate("═" * 50, :blue)
|
|
161
|
+
puts prompt.decorate(" #{message}", :blue, :bold)
|
|
162
|
+
puts prompt.decorate("═" * 50, :blue)
|
|
163
|
+
puts
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def review_reminder
|
|
167
|
+
puts
|
|
168
|
+
warning "IMPORTANT: Review the generated files carefully!"
|
|
169
|
+
puts " AI-generated context is a first draft. The quality of your future"
|
|
170
|
+
puts " AI interactions depends on the accuracy of these files."
|
|
171
|
+
puts
|
|
172
|
+
puts " Suggested review steps:"
|
|
173
|
+
puts " 1. Read through each generated file"
|
|
174
|
+
puts " 2. Fix any inaccuracies or missing information"
|
|
175
|
+
puts " 3. Add project-specific details the AI might have missed"
|
|
176
|
+
puts " 4. Remove any incorrect assumptions"
|
|
177
|
+
puts
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def format_size(bytes)
|
|
183
|
+
if bytes < 1024
|
|
184
|
+
"#{bytes} B"
|
|
185
|
+
elsif bytes < 1024 * 1024
|
|
186
|
+
"#{(bytes / 1024.0).round(1)} KB"
|
|
187
|
+
else
|
|
188
|
+
"#{(bytes / (1024.0 * 1024)).round(1)} MB"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyAiGemContext
|
|
4
|
+
# Base class for AI platform definitions
|
|
5
|
+
#
|
|
6
|
+
# Each platform (Claude, Cursor, Windsurf, etc.) defines:
|
|
7
|
+
# - The filename and location for context files
|
|
8
|
+
# - The template/format for the context file
|
|
9
|
+
# - The prompt to use when generating content
|
|
10
|
+
class Platform
|
|
11
|
+
class << self
|
|
12
|
+
# Human-readable name of the platform
|
|
13
|
+
def name
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# The filename for the context file (e.g., "CLAUDE.md", ".cursorrules")
|
|
18
|
+
def filename
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Where the file should be placed relative to project root
|
|
23
|
+
# Most platforms expect files at the project root
|
|
24
|
+
def path
|
|
25
|
+
filename
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Description shown in the interactive menu
|
|
29
|
+
def description
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The prompt template for generating content
|
|
34
|
+
# This tells the AI what format and structure to use
|
|
35
|
+
def generation_prompt
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Template content for empty boilerplate
|
|
40
|
+
def template
|
|
41
|
+
raise NotImplementedError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Check if the context file already exists
|
|
45
|
+
def exists?(project_root = Dir.pwd)
|
|
46
|
+
File.exist?(File.join(project_root, path))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Full path to the context file
|
|
50
|
+
def full_path(project_root = Dir.pwd)
|
|
51
|
+
File.join(project_root, path)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Write content to the context file
|
|
55
|
+
def write(content, project_root = Dir.pwd)
|
|
56
|
+
file_path = full_path(project_root)
|
|
57
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
|
58
|
+
File.write(file_path, content)
|
|
59
|
+
file_path
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Read existing content
|
|
63
|
+
def read(project_root = Dir.pwd)
|
|
64
|
+
return nil unless exists?(project_root)
|
|
65
|
+
|
|
66
|
+
File.read(full_path(project_root))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Create backup of existing file
|
|
70
|
+
def backup(project_root = Dir.pwd)
|
|
71
|
+
return nil unless exists?(project_root)
|
|
72
|
+
|
|
73
|
+
backup_path = "#{full_path(project_root)}.backup"
|
|
74
|
+
FileUtils.cp(full_path(project_root), backup_path)
|
|
75
|
+
backup_path
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyAiGemContext
|
|
4
|
+
module Platforms
|
|
5
|
+
class Claude < Platform
|
|
6
|
+
class << self
|
|
7
|
+
def name
|
|
8
|
+
"Claude Code"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def filename
|
|
12
|
+
"CLAUDE.md"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def description
|
|
16
|
+
"Claude Code / Claude AI - Project context file"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def generation_prompt
|
|
20
|
+
<<~PROMPT
|
|
21
|
+
Generate a CLAUDE.md file for this Ruby project. This file helps Claude Code understand the project.
|
|
22
|
+
|
|
23
|
+
Structure the file with these sections:
|
|
24
|
+
|
|
25
|
+
# Project Name
|
|
26
|
+
|
|
27
|
+
Brief description of what this project does.
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
How to set up and run the project (install deps, run tests, start server, etc.)
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
Key architectural decisions, folder structure, important patterns used.
|
|
36
|
+
|
|
37
|
+
## Code Style
|
|
38
|
+
|
|
39
|
+
Coding conventions, naming patterns, formatting preferences for this project.
|
|
40
|
+
|
|
41
|
+
## Common Tasks
|
|
42
|
+
|
|
43
|
+
Frequent development tasks and how to do them (run tests, lint, deploy, etc.)
|
|
44
|
+
|
|
45
|
+
## Key Files
|
|
46
|
+
|
|
47
|
+
Important files to understand the codebase (entry points, config, core classes).
|
|
48
|
+
|
|
49
|
+
## Gotchas
|
|
50
|
+
|
|
51
|
+
Non-obvious things, common mistakes, edge cases to watch out for.
|
|
52
|
+
|
|
53
|
+
Keep it concise and practical. Focus on information that helps an AI assistant understand and work with this codebase effectively.
|
|
54
|
+
PROMPT
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def template
|
|
58
|
+
<<~TEMPLATE
|
|
59
|
+
# Project Name
|
|
60
|
+
|
|
61
|
+
Brief description of what this project does.
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Install dependencies
|
|
67
|
+
bundle install
|
|
68
|
+
|
|
69
|
+
# Run tests
|
|
70
|
+
bundle exec rspec
|
|
71
|
+
|
|
72
|
+
# Start development
|
|
73
|
+
# Add commands here
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Architecture
|
|
77
|
+
|
|
78
|
+
Describe the key architectural decisions and patterns used in this project.
|
|
79
|
+
|
|
80
|
+
## Code Style
|
|
81
|
+
|
|
82
|
+
- Ruby style conventions used
|
|
83
|
+
- Naming patterns
|
|
84
|
+
- File organization
|
|
85
|
+
|
|
86
|
+
## Common Tasks
|
|
87
|
+
|
|
88
|
+
- **Run tests**: `bundle exec rspec`
|
|
89
|
+
- **Run linter**: `bundle exec rubocop`
|
|
90
|
+
- **Generate docs**: `bundle exec yard`
|
|
91
|
+
|
|
92
|
+
## Key Files
|
|
93
|
+
|
|
94
|
+
- `lib/` - Main library code
|
|
95
|
+
- `spec/` - Test files
|
|
96
|
+
- `Gemfile` - Dependencies
|
|
97
|
+
|
|
98
|
+
## Gotchas
|
|
99
|
+
|
|
100
|
+
- Things to watch out for
|
|
101
|
+
- Common mistakes
|
|
102
|
+
- Non-obvious behaviors
|
|
103
|
+
TEMPLATE
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyAiGemContext
|
|
4
|
+
module Platforms
|
|
5
|
+
class Codex < Platform
|
|
6
|
+
class << self
|
|
7
|
+
def name
|
|
8
|
+
"OpenAI Codex / ChatGPT"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def filename
|
|
12
|
+
"AGENTS.md"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def description
|
|
16
|
+
"OpenAI Codex / ChatGPT - Agents documentation"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def generation_prompt
|
|
20
|
+
<<~PROMPT
|
|
21
|
+
Generate an AGENTS.md file for this Ruby project. This file helps AI coding assistants (like ChatGPT, Codex, GitHub Copilot) understand this project.
|
|
22
|
+
|
|
23
|
+
Structure the file with:
|
|
24
|
+
|
|
25
|
+
# Project Name
|
|
26
|
+
|
|
27
|
+
Brief description of what this project does and its purpose.
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
How to install and configure the project.
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
Common usage patterns with code examples.
|
|
36
|
+
|
|
37
|
+
## Architecture
|
|
38
|
+
|
|
39
|
+
How the code is organized, key abstractions, design patterns.
|
|
40
|
+
|
|
41
|
+
## API Reference
|
|
42
|
+
|
|
43
|
+
Key classes/modules and their public interfaces.
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
How to contribute: running tests, code style, PR process.
|
|
48
|
+
|
|
49
|
+
## Examples
|
|
50
|
+
|
|
51
|
+
Practical code examples showing common use cases.
|
|
52
|
+
|
|
53
|
+
Focus on clarity and include runnable code examples where helpful.
|
|
54
|
+
PROMPT
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def template
|
|
58
|
+
<<~TEMPLATE
|
|
59
|
+
# Project Name
|
|
60
|
+
|
|
61
|
+
Brief description of what this project does.
|
|
62
|
+
|
|
63
|
+
## Setup
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Install
|
|
67
|
+
gem install project-name
|
|
68
|
+
# or add to Gemfile
|
|
69
|
+
gem 'project-name'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
require 'project_name'
|
|
76
|
+
|
|
77
|
+
# Basic usage example
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Architecture
|
|
81
|
+
|
|
82
|
+
Describe the overall architecture:
|
|
83
|
+
- Key modules and their responsibilities
|
|
84
|
+
- Design patterns used
|
|
85
|
+
- How components interact
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### MainClass
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# Constructor
|
|
93
|
+
MainClass.new(options)
|
|
94
|
+
|
|
95
|
+
# Key methods
|
|
96
|
+
instance.method_name(args)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Install dependencies
|
|
103
|
+
bundle install
|
|
104
|
+
|
|
105
|
+
# Run tests
|
|
106
|
+
bundle exec rspec
|
|
107
|
+
|
|
108
|
+
# Run linter
|
|
109
|
+
bundle exec rubocop
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Examples
|
|
113
|
+
|
|
114
|
+
### Example 1: Basic Usage
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# Show a complete working example
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Example 2: Advanced Usage
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# Show more complex usage
|
|
124
|
+
```
|
|
125
|
+
TEMPLATE
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyAiGemContext
|
|
4
|
+
module Platforms
|
|
5
|
+
class Cursor < Platform
|
|
6
|
+
class << self
|
|
7
|
+
def name
|
|
8
|
+
"Cursor"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def filename
|
|
12
|
+
".cursorrules"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def description
|
|
16
|
+
"Cursor IDE - Project rules file"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def generation_prompt
|
|
20
|
+
<<~PROMPT
|
|
21
|
+
Generate a .cursorrules file for this Ruby project. This file tells Cursor AI how to work with this codebase.
|
|
22
|
+
|
|
23
|
+
The format should be clear instructions/rules, NOT markdown headers. Write it as a set of directives.
|
|
24
|
+
|
|
25
|
+
Include:
|
|
26
|
+
|
|
27
|
+
1. Project overview - What this project is and does
|
|
28
|
+
2. Tech stack - Ruby version, key gems, frameworks
|
|
29
|
+
3. Code style rules - Naming conventions, formatting, patterns to follow
|
|
30
|
+
4. File organization - Where different types of code live
|
|
31
|
+
5. Testing approach - How to write tests, what framework is used
|
|
32
|
+
6. Common patterns - Idioms and patterns used in this codebase
|
|
33
|
+
7. Things to avoid - Anti-patterns, deprecated approaches
|
|
34
|
+
8. Key commands - How to run tests, lint, build, etc.
|
|
35
|
+
|
|
36
|
+
Write in a direct, instructional tone. Each rule should be actionable.
|
|
37
|
+
Keep it focused on practical guidance for generating and modifying code.
|
|
38
|
+
PROMPT
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def template
|
|
42
|
+
<<~TEMPLATE
|
|
43
|
+
# Project Overview
|
|
44
|
+
This is a Ruby project. Describe what it does here.
|
|
45
|
+
|
|
46
|
+
# Tech Stack
|
|
47
|
+
- Ruby 3.x
|
|
48
|
+
- RSpec for testing
|
|
49
|
+
- Add other key dependencies
|
|
50
|
+
|
|
51
|
+
# Code Style
|
|
52
|
+
- Use snake_case for methods and variables
|
|
53
|
+
- Use CamelCase for classes and modules
|
|
54
|
+
- Prefer single quotes for strings without interpolation
|
|
55
|
+
- Keep methods short and focused (under 10 lines ideally)
|
|
56
|
+
- Use meaningful variable names
|
|
57
|
+
|
|
58
|
+
# File Organization
|
|
59
|
+
- lib/ contains the main library code
|
|
60
|
+
- spec/ contains RSpec tests
|
|
61
|
+
- Each class gets its own file
|
|
62
|
+
|
|
63
|
+
# Testing
|
|
64
|
+
- Write RSpec tests for all new code
|
|
65
|
+
- Use describe/context/it blocks
|
|
66
|
+
- Prefer let over instance variables
|
|
67
|
+
- Test behavior, not implementation
|
|
68
|
+
|
|
69
|
+
# Patterns to Follow
|
|
70
|
+
- Dependency injection over global state
|
|
71
|
+
- Composition over inheritance
|
|
72
|
+
- Small, focused classes
|
|
73
|
+
|
|
74
|
+
# Things to Avoid
|
|
75
|
+
- Monkey patching core classes
|
|
76
|
+
- Global mutable state
|
|
77
|
+
- Deep inheritance hierarchies
|
|
78
|
+
|
|
79
|
+
# Commands
|
|
80
|
+
- Run tests: bundle exec rspec
|
|
81
|
+
- Run linter: bundle exec rubocop
|
|
82
|
+
- Install deps: bundle install
|
|
83
|
+
TEMPLATE
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|