rails_skills 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.
Files changed (26) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +99 -0
  4. data/Rakefile +5 -0
  5. data/lib/generators/rails_skills/commands_library/quality.md +16 -0
  6. data/lib/generators/rails_skills/install/install_generator.rb +211 -0
  7. data/lib/generators/rails_skills/install/templates/agents/api-dev.md.tt +28 -0
  8. data/lib/generators/rails_skills/install/templates/agents/fullstack-dev.md.tt +28 -0
  9. data/lib/generators/rails_skills/install/templates/agents/rails-developer.md.tt +22 -0
  10. data/lib/generators/rails_skills/install/templates/settings.local.json.tt +11 -0
  11. data/lib/generators/rails_skills/rules_library/code-style.md +28 -0
  12. data/lib/generators/rails_skills/rules_library/security.md +33 -0
  13. data/lib/generators/rails_skills/rules_library/testing.md +27 -0
  14. data/lib/generators/rails_skills/skill/skill_generator.rb +46 -0
  15. data/lib/generators/rails_skills/skill/templates/SKILL.md.tt +29 -0
  16. data/lib/generators/rails_skills/skills_library/rails-api-controllers/SKILL.md +106 -0
  17. data/lib/generators/rails_skills/skills_library/rails-controllers/SKILL.md +125 -0
  18. data/lib/generators/rails_skills/skills_library/rails-hotwire/SKILL.md +89 -0
  19. data/lib/generators/rails_skills/skills_library/rails-jobs/SKILL.md +39 -0
  20. data/lib/generators/rails_skills/skills_library/rails-models/SKILL.md +105 -0
  21. data/lib/generators/rails_skills/skills_library/rails-views/SKILL.md +109 -0
  22. data/lib/generators/rails_skills/skills_library/rspec-testing/SKILL.md +105 -0
  23. data/lib/rails_skills/railtie.rb +10 -0
  24. data/lib/rails_skills/version.rb +5 -0
  25. data/lib/rails_skills.rb +12 -0
  26. metadata +86 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 178573c6ee15fecc50390d962b0497fe63f76caa23a763105bb02cf2bd035742
4
+ data.tar.gz: 38fe007e723b016ea6ba499681baa7e76b2cdadbe59d8221697e60cd3719c31b
5
+ SHA512:
6
+ metadata.gz: 58f7d7ea5fd40d5467f11182985ccecd43395f0a2e31aad41894d9a955088ef8c577e93568be38667a2a08e7f66f59bfea57995ad7fcb6a6993f90d7e3e46ad6
7
+ data.tar.gz: 1303af0ddab82bb8c0eae537997ca52b5ecf945bc1df025f23da37bf027d774826591249a0c7164bd61cdbe3ba088d23a454c17e7ce9036f430bd84ce1d9a97a
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sergii Mostovyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # RailsSkills
2
+
3
+ A Ruby gem that organizes AI skills for Rails projects, shared between **Claude** and **Codex**.
4
+
5
+ RailsSkills creates a `skills/` directory in your Rails root as the canonical location for AI skill definitions, then symlinks it into `.claude/skills` and `.codex/skills` so both AI assistants share the same knowledge base.
6
+
7
+ ## Installation
8
+
9
+ Add to your Gemfile:
10
+
11
+ ```ruby
12
+ gem "rails_skills"
13
+ ```
14
+
15
+ Then run:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Install with default (basic) preset
24
+
25
+ ```bash
26
+ rails generate rails_skills:install
27
+ ```
28
+
29
+ ### Install with a preset
30
+
31
+ ```bash
32
+ rails generate rails_skills:install --preset=basic
33
+ rails generate rails_skills:install --preset=fullstack
34
+ rails generate rails_skills:install --preset=api
35
+ ```
36
+
37
+ ### What it creates
38
+
39
+ ```
40
+ your_rails_app/
41
+ ├── skills/ # Shared AI skill files (canonical location)
42
+ │ ├── rails-models/
43
+ │ │ └── SKILL.md
44
+ │ ├── rails-controllers/
45
+ │ │ └── SKILL.md
46
+ │ └── rails-views/
47
+ │ └── SKILL.md
48
+ ├── .claude/
49
+ │ ├── skills -> ../skills # Symlink to shared skills
50
+ │ ├── agents/
51
+ │ ├── commands/
52
+ │ ├── rules/
53
+ │ └── settings.local.json
54
+ └── .codex/
55
+ └── skills -> ../skills # Symlink to shared skills
56
+ ```
57
+
58
+ Both Claude and Codex read from the same `skills/` directory via symlinks.
59
+
60
+ ### Presets
61
+
62
+ | Preset | Skills included |
63
+ |--------|----------------|
64
+ | **basic** | rails-models, rails-controllers, rails-views |
65
+ | **fullstack** | basic + rails-hotwire, rspec-testing |
66
+ | **api** | rails-models, rails-api-controllers, rspec-testing |
67
+
68
+ ### Create a custom skill
69
+
70
+ ```bash
71
+ rails generate rails_skills:skill my_custom_skill
72
+ rails generate rails_skills:skill my_custom_skill --description="My skill description"
73
+ rails generate rails_skills:skill my_custom_skill --with-references
74
+ ```
75
+
76
+ Skills are created in `skills/` and automatically available to both Claude and Codex.
77
+
78
+ ## Skill Library
79
+
80
+ The gem ships with these pre-built skills:
81
+
82
+ - **rails-models** - ActiveRecord patterns, migrations, validations, associations
83
+ - **rails-controllers** - Controller actions, routing, REST conventions, filters
84
+ - **rails-views** - ERB templates, layouts, partials, forms, helpers
85
+ - **rails-hotwire** - Turbo Drive, Turbo Frames, Turbo Streams, Stimulus
86
+ - **rails-api-controllers** - API-only controllers, serialization, versioning
87
+ - **rspec-testing** - RSpec patterns for models, requests, and system tests
88
+ - **rails-jobs** - Active Job patterns, background processing
89
+
90
+ ## How It Works
91
+
92
+ 1. `skills/` is the single source of truth for AI skill files
93
+ 2. `.claude/skills` is a symlink pointing to `../skills`
94
+ 3. `.codex/skills` is a symlink pointing to `../skills`
95
+ 4. Edit skills in `skills/` and both AI tools see the changes immediately
96
+
97
+ ## License
98
+
99
+ MIT License. See [LICENSE](LICENSE) for details.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ task default: :spec
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Run code quality checks
3
+ allowed-tools: Bash, Read
4
+ ---
5
+
6
+ ## Quality Check
7
+
8
+ Run the following quality checks on the project and report results:
9
+
10
+ 1. **Linting**: Run `bundle exec rubocop` and report any violations
11
+ 2. **Tests**: Run `bundle exec rspec` and report failures
12
+ 3. **Security**: Run `bundle exec brakeman --no-pager` if available
13
+
14
+ Summarize findings and suggest fixes for any issues found.
15
+
16
+ Use $ARGUMENTS to scope the check (e.g., a specific file or directory).
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsSkills
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ class_option :preset, type: :string, default: "basic",
11
+ desc: "Preset bundle (basic, fullstack, api)"
12
+ class_option :skip_agents, type: :boolean, default: false,
13
+ desc: "Don't create default agents"
14
+
15
+ def create_skills_directory
16
+ empty_directory "skills"
17
+ end
18
+
19
+ def create_claude_directory
20
+ empty_directory ".claude"
21
+ empty_directory ".claude/commands"
22
+ empty_directory ".claude/rules"
23
+ empty_directory ".claude/agents" unless options[:skip_agents]
24
+ end
25
+
26
+ def create_codex_directory
27
+ empty_directory ".codex"
28
+ end
29
+
30
+ def create_symlinks
31
+ create_symlink(".claude/skills", "../skills")
32
+ create_symlink(".codex/skills", "../skills")
33
+ end
34
+
35
+ def copy_claude_settings
36
+ template "settings.local.json.tt", ".claude/settings.local.json"
37
+ end
38
+
39
+ def install_preset_skills
40
+ case options[:preset]
41
+ when "fullstack"
42
+ install_fullstack_preset
43
+ when "api"
44
+ install_api_preset
45
+ else
46
+ install_basic_preset
47
+ end
48
+ end
49
+
50
+ def show_instructions
51
+ say ""
52
+ say "RailsSkills installed successfully!", :green
53
+ say ""
54
+ say "Directory structure:", :yellow
55
+ say " skills/ <- shared AI skill files (canonical location)"
56
+ say " .claude/skills -> ../skills (symlink)"
57
+ say " .codex/skills -> ../skills (symlink)"
58
+ say ""
59
+ say "Both Claude and Codex now share the same skills from skills/", :blue
60
+ say ""
61
+ say "Next steps:", :yellow
62
+ say " rails g rails_skills:skill my_skill # Create a custom skill"
63
+ say ""
64
+ end
65
+
66
+ private
67
+
68
+ def create_symlink(link_path, target)
69
+ dest = File.join(destination_root, link_path)
70
+ if File.symlink?(dest) || File.exist?(dest)
71
+ say_status :exist, link_path, :blue
72
+ else
73
+ File.symlink(target, dest)
74
+ say_status :symlink, "#{link_path} -> #{target}", :green
75
+ end
76
+ end
77
+
78
+ def install_basic_preset
79
+ say "Installing basic preset...", :green
80
+ install_skill("rails-models")
81
+ install_skill("rails-controllers")
82
+ install_skill("rails-views")
83
+ install_rule("code-style")
84
+ install_rule("testing")
85
+ install_command("quality")
86
+ create_basic_agent unless options[:skip_agents]
87
+ end
88
+
89
+ def install_fullstack_preset
90
+ say "Installing fullstack preset...", :green
91
+ install_skill("rails-models")
92
+ install_skill("rails-controllers")
93
+ install_skill("rails-views")
94
+ install_skill("rails-hotwire")
95
+ install_skill("rspec-testing")
96
+ install_rule("code-style")
97
+ install_rule("testing")
98
+ install_rule("security")
99
+ install_command("quality")
100
+ create_fullstack_agent unless options[:skip_agents]
101
+ end
102
+
103
+ def install_api_preset
104
+ say "Installing API preset...", :green
105
+ install_skill("rails-models")
106
+ install_skill("rails-api-controllers")
107
+ install_skill("rspec-testing")
108
+ install_rule("code-style")
109
+ install_rule("testing")
110
+ install_rule("security")
111
+ install_command("quality")
112
+ create_api_agent unless options[:skip_agents]
113
+ end
114
+
115
+ def install_skill(skill_name)
116
+ skill_dir = "skills/#{skill_name}"
117
+ empty_directory skill_dir
118
+
119
+ skill_source = File.expand_path("../skills_library/#{skill_name}", __dir__)
120
+ if File.directory?(skill_source)
121
+ directory skill_source, skill_dir
122
+ else
123
+ create_file "#{skill_dir}/SKILL.md", default_skill_content(skill_name)
124
+ end
125
+ end
126
+
127
+ def install_rule(rule_name)
128
+ rule_file = ".claude/rules/#{rule_name}.md"
129
+ rule_source = File.expand_path("../rules_library/#{rule_name}.md", __dir__)
130
+
131
+ if File.exist?(rule_source)
132
+ copy_file rule_source, rule_file
133
+ else
134
+ create_file rule_file, default_rule_content(rule_name)
135
+ end
136
+ end
137
+
138
+ def install_command(command_name)
139
+ command_file = ".claude/commands/#{command_name}.md"
140
+ command_source = File.expand_path("../commands_library/#{command_name}.md", __dir__)
141
+
142
+ if File.exist?(command_source)
143
+ copy_file command_source, command_file
144
+ else
145
+ create_file command_file, default_command_content(command_name)
146
+ end
147
+ end
148
+
149
+ def create_basic_agent
150
+ template "agents/rails-developer.md.tt", ".claude/agents/rails-developer.md"
151
+ end
152
+
153
+ def create_fullstack_agent
154
+ template "agents/fullstack-dev.md.tt", ".claude/agents/fullstack-dev.md"
155
+ end
156
+
157
+ def create_api_agent
158
+ template "agents/api-dev.md.tt", ".claude/agents/api-dev.md"
159
+ end
160
+
161
+ def default_skill_content(skill_name)
162
+ <<~SKILL
163
+ ---
164
+ name: #{skill_name}
165
+ description: #{skill_name.tr('-', ' ').capitalize} skill
166
+ version: 1.0.0
167
+ ---
168
+
169
+ # #{skill_name.tr('-', ' ').capitalize}
170
+
171
+ Add your skill content here.
172
+ SKILL
173
+ end
174
+
175
+ def default_rule_content(rule_name)
176
+ <<~RULE
177
+ # #{rule_name.tr('-', ' ').capitalize} Rules
178
+
179
+ Add your project rules and guidelines here.
180
+ RULE
181
+ end
182
+
183
+ def default_command_content(command_name)
184
+ <<~COMMAND
185
+ ---
186
+ description: #{command_name.tr('-', ' ').capitalize} command
187
+ allowed-tools: Bash, Read, Edit, Write
188
+ ---
189
+
190
+ ## #{command_name.tr('-', ' ').capitalize}
191
+
192
+ Add your command instructions here.
193
+
194
+ Use $ARGUMENTS to reference command arguments.
195
+ COMMAND
196
+ end
197
+
198
+ def app_name
199
+ Rails.application.class.module_parent_name
200
+ rescue StandardError
201
+ "MyApp"
202
+ end
203
+
204
+ def rails_version
205
+ Rails::VERSION::STRING
206
+ rescue StandardError
207
+ "7.0"
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,28 @@
1
+ # API Developer Agent
2
+
3
+ You are an API-focused Rails developer working on **<%= app_name %>** (Rails <%= rails_version %>).
4
+
5
+ ## Skills
6
+
7
+ Load and follow all skills from the `skills/` directory, with emphasis on:
8
+ - Rails models
9
+ - API controllers
10
+ - RSpec testing
11
+
12
+ ## Guidelines
13
+
14
+ - Build RESTful JSON APIs
15
+ - Use proper HTTP status codes
16
+ - Implement authentication and authorization
17
+ - Write comprehensive API tests
18
+ - Document endpoints clearly
19
+ - Follow JSON:API or similar conventions
20
+
21
+ ## Workflow
22
+
23
+ 1. Understand the API requirement
24
+ 2. Design the endpoint (HTTP method, path, request/response format)
25
+ 3. Start with the model and migrations
26
+ 4. Build the API controller
27
+ 5. Write request specs
28
+ 6. Document the endpoint
@@ -0,0 +1,28 @@
1
+ # Fullstack Developer Agent
2
+
3
+ You are a fullstack Rails developer working on **<%= app_name %>** (Rails <%= rails_version %>).
4
+
5
+ ## Skills
6
+
7
+ Load and follow all skills from the `skills/` directory, with emphasis on:
8
+ - Rails models, controllers, and views
9
+ - Hotwire (Turbo + Stimulus)
10
+ - RSpec testing
11
+
12
+ ## Guidelines
13
+
14
+ - Follow Rails conventions and best practices
15
+ - Build reactive UIs with Turbo Frames and Turbo Streams
16
+ - Use Stimulus for JavaScript behavior
17
+ - Write comprehensive RSpec tests
18
+ - Use the project's existing patterns and style
19
+
20
+ ## Workflow
21
+
22
+ 1. Understand the requirement
23
+ 2. Plan the full-stack implementation (model, controller, views)
24
+ 3. Start with the model and migrations
25
+ 4. Build the controller with proper actions
26
+ 5. Create views with Hotwire integration
27
+ 6. Write RSpec tests for all layers
28
+ 7. Verify everything works end-to-end
@@ -0,0 +1,22 @@
1
+ # Rails Developer Agent
2
+
3
+ You are a Rails developer working on **<%= app_name %>** (Rails <%= rails_version %>).
4
+
5
+ ## Skills
6
+
7
+ Load and follow all skills from the `skills/` directory.
8
+
9
+ ## Guidelines
10
+
11
+ - Follow Rails conventions and best practices
12
+ - Write clean, tested code
13
+ - Use the project's existing patterns and style
14
+ - Prefer simple solutions over clever ones
15
+
16
+ ## Workflow
17
+
18
+ 1. Understand the requirement
19
+ 2. Check existing code for patterns
20
+ 3. Implement following Rails conventions
21
+ 4. Write tests
22
+ 5. Verify everything works
@@ -0,0 +1,11 @@
1
+ {
2
+ "skills": {
3
+ "autoLoad": true,
4
+ "path": ".claude/skills"
5
+ },
6
+ "agents": {
7
+ "path": ".claude/agents"
8
+ },
9
+ "project": "<%= app_name %>",
10
+ "rails_version": "<%= rails_version %>"
11
+ }
@@ -0,0 +1,28 @@
1
+ # Code Style Rules
2
+
3
+ ## Ruby Style
4
+
5
+ - Use 2 spaces for indentation (not tabs)
6
+ - Prefer double quotes for strings
7
+ - Use modern Ruby syntax (Ruby 3.0+)
8
+ - Prefer `&.` safe navigation over conditional checks
9
+ - Use trailing commas in multi-line arrays and hashes
10
+
11
+ ## Rails Conventions
12
+
13
+ - Follow RESTful routing patterns
14
+ - Keep controllers thin, models focused
15
+ - Use `before_action` for shared controller setup
16
+ - Use strong parameters for mass assignment protection
17
+ - Prefer ActiveRecord query methods over raw SQL
18
+
19
+ ## Naming
20
+
21
+ - snake_case for variables, methods, and file names
22
+ - PascalCase for classes and modules
23
+ - Prefix boolean methods with `?` (e.g., `published?`)
24
+ - Prefix destructive methods with `!` (e.g., `publish!`)
25
+
26
+ ## Auto-Fixing
27
+
28
+ Run `bundle exec rubocop -a` to fix style violations before committing.
@@ -0,0 +1,33 @@
1
+ # Security Rules
2
+
3
+ ## Input Handling
4
+
5
+ - Always use strong parameters in controllers
6
+ - Never trust user input - validate and sanitize
7
+ - Use parameterized queries (ActiveRecord handles this by default)
8
+ - Never interpolate user input into SQL strings
9
+
10
+ ## Authentication
11
+
12
+ - Use established libraries (Devise, etc.) for authentication
13
+ - Store passwords with bcrypt (`has_secure_password`)
14
+ - Use CSRF protection (enabled by default in Rails)
15
+ - Implement proper session management
16
+
17
+ ## Authorization
18
+
19
+ - Check permissions in every controller action
20
+ - Use authorization libraries (Pundit, CanCanCan) for complex rules
21
+ - Never rely on hiding UI elements as a security measure
22
+
23
+ ## Output
24
+
25
+ - ERB auto-escapes output by default - don't bypass with `raw` or `html_safe` unless necessary
26
+ - Sanitize HTML content with `sanitize` helper
27
+ - Set proper Content Security Policy headers
28
+
29
+ ## Secrets
30
+
31
+ - Never commit secrets, API keys, or credentials to git
32
+ - Use Rails credentials (`rails credentials:edit`) or environment variables
33
+ - Add sensitive files to `.gitignore`
@@ -0,0 +1,27 @@
1
+ # Testing Rules
2
+
3
+ ## General
4
+
5
+ - Every feature must have tests
6
+ - Test behavior, not implementation details
7
+ - Keep tests fast and independent
8
+ - Use factories (FactoryBot) over fixtures
9
+
10
+ ## Test Types
11
+
12
+ - **Model specs** for validations, associations, and business logic
13
+ - **Request specs** for controller actions and API endpoints
14
+ - **System specs** for user-facing workflows (use sparingly)
15
+
16
+ ## Conventions
17
+
18
+ - One assertion per test when practical
19
+ - Use `let` for lazy setup, `let!` when eager evaluation is needed
20
+ - Use `describe` for the subject, `context` for conditions, `it` for behavior
21
+ - Name tests clearly: `it "returns published posts for active users"`
22
+
23
+ ## Running Tests
24
+
25
+ - `bundle exec rspec` to run all tests
26
+ - `bundle exec rspec spec/models/` to run model specs
27
+ - `bundle exec rspec spec/file.rb:42` to run a specific test
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsSkills
6
+ module Generators
7
+ class SkillGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ class_option :description, type: :string, default: nil,
11
+ desc: "Skill description"
12
+ class_option :with_references, type: :boolean, default: false,
13
+ desc: "Create references directory"
14
+
15
+ def create_skill_directory
16
+ empty_directory skill_path
17
+ end
18
+
19
+ def create_skill_file
20
+ template "SKILL.md.tt", "#{skill_path}/SKILL.md"
21
+ end
22
+
23
+ def create_references_directory
24
+ return unless options[:with_references]
25
+
26
+ empty_directory "#{skill_path}/references"
27
+ create_file "#{skill_path}/references/.keep", ""
28
+ end
29
+
30
+ def show_instructions
31
+ say "\nSkill '#{file_name}' created in #{skill_path}/", :green
32
+ say "This skill is available to both Claude and Codex via symlinks.", :blue
33
+ end
34
+
35
+ private
36
+
37
+ def skill_path
38
+ "skills/#{file_name}"
39
+ end
40
+
41
+ def skill_description
42
+ options[:description] || "Custom skill for #{file_name.tr('-', ' ')}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: <%= file_name %>
3
+ description: <%= skill_description %>
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # <%= file_name.tr('-', ' ').split.map(&:capitalize).join(' ') %>
8
+
9
+ ## Quick Reference
10
+
11
+ | Command | Purpose |
12
+ |---------|---------|
13
+ | TODO | Add commands here |
14
+
15
+ ## Overview
16
+
17
+ <%= skill_description %>
18
+
19
+ ## Usage
20
+
21
+ Add usage examples and patterns here.
22
+
23
+ ## Common Patterns
24
+
25
+ Add frequently used code patterns here.
26
+
27
+ ## Best Practices
28
+
29
+ Add best practices and conventions here.