claude-agent-framework 1.0.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 (111) hide show
  1. package/README.md +128 -0
  2. package/bin/claude-framework +3 -0
  3. package/framework/agents/design-lead.md +240 -0
  4. package/framework/agents/product-owner.md +179 -0
  5. package/framework/agents/tech-lead.md +226 -0
  6. package/framework/commands/ayuda.md +127 -0
  7. package/framework/commands/a/303/261adir.md +98 -0
  8. package/framework/commands/backup.md +397 -0
  9. package/framework/commands/cambiar.md +110 -0
  10. package/framework/commands/cloud.md +457 -0
  11. package/framework/commands/code.md +142 -0
  12. package/framework/commands/debug.md +334 -0
  13. package/framework/commands/deploy.md +383 -0
  14. package/framework/commands/deshacer.md +120 -0
  15. package/framework/commands/estado.md +218 -0
  16. package/framework/commands/explica.md +227 -0
  17. package/framework/commands/feature.md +120 -0
  18. package/framework/commands/git.md +427 -0
  19. package/framework/commands/historial.md +202 -0
  20. package/framework/commands/learn.md +408 -0
  21. package/framework/commands/movil.md +245 -0
  22. package/framework/commands/nuevo.md +118 -0
  23. package/framework/commands/plan.md +134 -0
  24. package/framework/commands/prd.md +113 -0
  25. package/framework/commands/probar.md +148 -0
  26. package/framework/commands/revisar.md +208 -0
  27. package/framework/commands/seeds.md +230 -0
  28. package/framework/commands/seguridad.md +226 -0
  29. package/framework/commands/tasks.md +157 -0
  30. package/framework/skills/architecture/algorithms.md +970 -0
  31. package/framework/skills/architecture/clean-code.md +1080 -0
  32. package/framework/skills/architecture/design-patterns.md +1984 -0
  33. package/framework/skills/architecture/functional-programming.md +972 -0
  34. package/framework/skills/architecture/solid.md +991 -0
  35. package/framework/skills/cloud/cloud-aws.md +848 -0
  36. package/framework/skills/cloud/cloud-azure.md +931 -0
  37. package/framework/skills/cloud/cloud-gcp.md +848 -0
  38. package/framework/skills/cloud/message-queues.md +1229 -0
  39. package/framework/skills/core/accessibility.md +401 -0
  40. package/framework/skills/core/api.md +474 -0
  41. package/framework/skills/core/authentication.md +306 -0
  42. package/framework/skills/core/authorization.md +388 -0
  43. package/framework/skills/core/background-jobs.md +341 -0
  44. package/framework/skills/core/caching.md +473 -0
  45. package/framework/skills/core/code-review.md +341 -0
  46. package/framework/skills/core/controllers.md +290 -0
  47. package/framework/skills/core/cua.md +285 -0
  48. package/framework/skills/core/documentation.md +472 -0
  49. package/framework/skills/core/file-uploads.md +351 -0
  50. package/framework/skills/core/hotwire-native.md +296 -0
  51. package/framework/skills/core/hotwire.md +278 -0
  52. package/framework/skills/core/i18n.md +334 -0
  53. package/framework/skills/core/imports-exports.md +750 -0
  54. package/framework/skills/core/infrastructure.md +337 -0
  55. package/framework/skills/core/models.md +228 -0
  56. package/framework/skills/core/notifications.md +672 -0
  57. package/framework/skills/core/payments.md +581 -0
  58. package/framework/skills/core/performance.md +361 -0
  59. package/framework/skills/core/rails-scaffold.md +131 -0
  60. package/framework/skills/core/search.md +518 -0
  61. package/framework/skills/core/security.md +565 -0
  62. package/framework/skills/core/seeds.md +307 -0
  63. package/framework/skills/core/seo.md +542 -0
  64. package/framework/skills/core/testing.md +393 -0
  65. package/framework/skills/core/views.md +260 -0
  66. package/framework/skills/core/websockets.md +564 -0
  67. package/framework/skills/data/advanced-sql.md +1204 -0
  68. package/framework/skills/data/nosql.md +1141 -0
  69. package/framework/skills/devops/containers-advanced.md +1237 -0
  70. package/framework/skills/devops/debugging.md +834 -0
  71. package/framework/skills/devops/git-workflow.md +752 -0
  72. package/framework/skills/devops/networking.md +932 -0
  73. package/framework/skills/devops/shell-scripting.md +1132 -0
  74. package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
  75. package/framework/sub-agents/cloud-agent.md +677 -0
  76. package/framework/sub-agents/data.md +504 -0
  77. package/framework/sub-agents/debugging-agent.md +554 -0
  78. package/framework/sub-agents/devops.md +483 -0
  79. package/framework/sub-agents/docs.md +176 -0
  80. package/framework/sub-agents/frontend-dev.md +349 -0
  81. package/framework/sub-agents/git-workflow-agent.md +697 -0
  82. package/framework/sub-agents/integrations.md +630 -0
  83. package/framework/sub-agents/native-dev.md +434 -0
  84. package/framework/sub-agents/qa.md +138 -0
  85. package/framework/sub-agents/rails-dev.md +375 -0
  86. package/framework/sub-agents/security.md +526 -0
  87. package/framework/sub-agents/ui.md +437 -0
  88. package/framework/sub-agents/ux.md +284 -0
  89. package/framework/templates/api-spec.md +500 -0
  90. package/framework/templates/component-spec.md +248 -0
  91. package/framework/templates/feature.json +13 -0
  92. package/framework/templates/model-spec.md +318 -0
  93. package/framework/templates/prd-template.md +80 -0
  94. package/framework/templates/task-plan.md +122 -0
  95. package/framework/templates/task-user-story.md +52 -0
  96. package/framework/templates/technical-spec.md +260 -0
  97. package/framework/templates/user-story.md +95 -0
  98. package/package.json +42 -0
  99. package/project-templates/CLAUDE.md +42 -0
  100. package/project-templates/contexts/architecture.md +25 -0
  101. package/project-templates/contexts/conventions.md +46 -0
  102. package/project-templates/contexts/design-system.md +47 -0
  103. package/project-templates/contexts/requirements.md +38 -0
  104. package/project-templates/contexts/stack.md +30 -0
  105. package/project-templates/history/active/models.md +11 -0
  106. package/project-templates/history/changelog.md +15 -0
  107. package/project-templates/workspace/.gitkeep +0 -0
  108. package/src/cli.js +52 -0
  109. package/src/init.js +104 -0
  110. package/src/status.js +75 -0
  111. package/src/update.js +88 -0
@@ -0,0 +1,278 @@
1
+ # Skill: Hotwire
2
+
3
+ ## Purpose
4
+ Implement dynamic, SPA-like interactions using Turbo (Drive, Frames, Streams) and Stimulus without writing custom JavaScript.
5
+
6
+ ## Turbo Drive
7
+
8
+ ### Automatic Enhancement
9
+ Turbo Drive is enabled by default. All links and forms are enhanced automatically.
10
+
11
+ ### Disabling Turbo
12
+ ```erb
13
+ <%# Disable for specific link %>
14
+ <%= link_to "External", "https://example.com", data: { turbo: false } %>
15
+
16
+ <%# Disable for form %>
17
+ <%= form_with model: @article, data: { turbo: false } do |f| %>
18
+ <% end %>
19
+ ```
20
+
21
+ ### Progress Bar
22
+ ```css
23
+ /* Customize in application.css */
24
+ .turbo-progress-bar {
25
+ height: 3px;
26
+ background-color: #3b82f6;
27
+ }
28
+ ```
29
+
30
+ ## Turbo Frames
31
+
32
+ ### Basic Frame
33
+ ```erb
34
+ <%# index.html.erb %>
35
+ <%= turbo_frame_tag "articles" do %>
36
+ <%= render @articles %>
37
+ <% end %>
38
+
39
+ <%# _article.html.erb %>
40
+ <%= turbo_frame_tag dom_id(article) do %>
41
+ <div class="article-card">
42
+ <%= article.title %>
43
+ <%= link_to "Edit", edit_article_path(article) %>
44
+ </div>
45
+ <% end %>
46
+
47
+ <%# edit.html.erb %>
48
+ <%= turbo_frame_tag dom_id(@article) do %>
49
+ <%= render "form", article: @article %>
50
+ <% end %>
51
+ ```
52
+
53
+ ### Lazy Loading
54
+ ```erb
55
+ <%= turbo_frame_tag "comments",
56
+ src: article_comments_path(@article),
57
+ loading: :lazy do %>
58
+ <p>Loading comments...</p>
59
+ <% end %>
60
+ ```
61
+
62
+ ### Breaking Out of Frames
63
+ ```erb
64
+ <%# Target entire page %>
65
+ <%= link_to "View All", articles_path, data: { turbo_frame: "_top" } %>
66
+
67
+ <%# Target specific frame %>
68
+ <%= link_to "Details", article_path(article), data: { turbo_frame: "main_content" } %>
69
+ ```
70
+
71
+ ### Frame Targeting
72
+ ```erb
73
+ <%# Form targets different frame %>
74
+ <%= form_with model: @comment, data: { turbo_frame: "comments" } do |f| %>
75
+ <%= f.text_area :body %>
76
+ <%= f.submit %>
77
+ <% end %>
78
+ ```
79
+
80
+ ## Turbo Streams
81
+
82
+ ### Stream Actions
83
+ ```erb
84
+ <%# append - Add to end %>
85
+ <%= turbo_stream.append "articles", @article %>
86
+
87
+ <%# prepend - Add to beginning %>
88
+ <%= turbo_stream.prepend "articles", @article %>
89
+
90
+ <%# replace - Replace entire element %>
91
+ <%= turbo_stream.replace @article %>
92
+
93
+ <%# update - Replace contents only %>
94
+ <%= turbo_stream.update "counter", "42" %>
95
+
96
+ <%# remove - Delete element %>
97
+ <%= turbo_stream.remove @article %>
98
+
99
+ <%# before/after - Insert adjacent %>
100
+ <%= turbo_stream.before @article, partial: "divider" %>
101
+ ```
102
+
103
+ ### Controller Response
104
+ ```ruby
105
+ def create
106
+ @article = current_user.articles.build(article_params)
107
+
108
+ if @article.save
109
+ respond_to do |format|
110
+ format.html { redirect_to @article }
111
+ format.turbo_stream
112
+ end
113
+ else
114
+ render :new, status: :unprocessable_entity
115
+ end
116
+ end
117
+ ```
118
+
119
+ ### Stream Template
120
+ ```erb
121
+ <%# app/views/articles/create.turbo_stream.erb %>
122
+ <%= turbo_stream.prepend "articles", @article %>
123
+ <%= turbo_stream.update "articles_count", Article.count %>
124
+ <%= turbo_stream.replace "new_article_form" do %>
125
+ <%= render "form", article: Article.new %>
126
+ <% end %>
127
+ ```
128
+
129
+ ### Inline Streams
130
+ ```ruby
131
+ def destroy
132
+ @article.destroy
133
+
134
+ render turbo_stream: turbo_stream.remove(@article)
135
+ end
136
+
137
+ # Multiple streams
138
+ render turbo_stream: [
139
+ turbo_stream.remove(@article),
140
+ turbo_stream.update("count", Article.count)
141
+ ]
142
+ ```
143
+
144
+ ## Stimulus Controllers
145
+
146
+ ### Basic Controller
147
+ ```javascript
148
+ // app/javascript/controllers/hello_controller.js
149
+ import { Controller } from "@hotwired/stimulus"
150
+
151
+ export default class extends Controller {
152
+ static targets = ["name", "output"]
153
+ static values = { greeting: { type: String, default: "Hello" } }
154
+
155
+ greet() {
156
+ this.outputTarget.textContent = `${this.greetingValue}, ${this.nameTarget.value}!`
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### HTML Usage
162
+ ```erb
163
+ <div data-controller="hello" data-hello-greeting-value="Hi">
164
+ <input data-hello-target="name" type="text">
165
+ <button data-action="click->hello#greet">Greet</button>
166
+ <span data-hello-target="output"></span>
167
+ </div>
168
+ ```
169
+
170
+ ### Common Controllers
171
+
172
+ #### Toggle
173
+ ```javascript
174
+ // toggle_controller.js
175
+ import { Controller } from "@hotwired/stimulus"
176
+
177
+ export default class extends Controller {
178
+ static targets = ["content"]
179
+ static classes = ["hidden"]
180
+
181
+ toggle() {
182
+ this.contentTarget.classList.toggle(this.hiddenClass)
183
+ }
184
+ }
185
+ ```
186
+
187
+ #### Flash Auto-Dismiss
188
+ ```javascript
189
+ // flash_controller.js
190
+ import { Controller } from "@hotwired/stimulus"
191
+
192
+ export default class extends Controller {
193
+ connect() {
194
+ setTimeout(() => this.dismiss(), 5000)
195
+ }
196
+
197
+ dismiss() {
198
+ this.element.remove()
199
+ }
200
+ }
201
+ ```
202
+
203
+ #### Form Validation
204
+ ```javascript
205
+ // form_validation_controller.js
206
+ import { Controller } from "@hotwired/stimulus"
207
+
208
+ export default class extends Controller {
209
+ static targets = ["submit"]
210
+
211
+ validate(event) {
212
+ const form = event.target.closest("form")
213
+ this.submitTarget.disabled = !form.checkValidity()
214
+ }
215
+ }
216
+ ```
217
+
218
+ #### Dropdown
219
+ ```javascript
220
+ // dropdown_controller.js
221
+ import { Controller } from "@hotwired/stimulus"
222
+
223
+ export default class extends Controller {
224
+ static targets = ["menu"]
225
+
226
+ toggle() {
227
+ this.menuTarget.classList.toggle("hidden")
228
+ }
229
+
230
+ close(event) {
231
+ if (!this.element.contains(event.target)) {
232
+ this.menuTarget.classList.add("hidden")
233
+ }
234
+ }
235
+
236
+ connect() {
237
+ document.addEventListener("click", this.close.bind(this))
238
+ }
239
+
240
+ disconnect() {
241
+ document.removeEventListener("click", this.close.bind(this))
242
+ }
243
+ }
244
+ ```
245
+
246
+ ## Real-Time with Turbo Streams
247
+
248
+ ### Broadcast from Model
249
+ ```ruby
250
+ class Comment < ApplicationRecord
251
+ belongs_to :article
252
+
253
+ after_create_commit -> {
254
+ broadcast_prepend_to article, target: "comments"
255
+ }
256
+
257
+ after_destroy_commit -> {
258
+ broadcast_remove_to article
259
+ }
260
+ end
261
+ ```
262
+
263
+ ### View Subscription
264
+ ```erb
265
+ <%= turbo_stream_from @article %>
266
+
267
+ <div id="comments">
268
+ <%= render @article.comments %>
269
+ </div>
270
+ ```
271
+
272
+ ## Best Practices
273
+
274
+ 1. **Use frames for in-place editing** - Edit forms replace show content
275
+ 2. **Use streams for lists** - Append/prepend without full reload
276
+ 3. **Keep Stimulus simple** - Small, focused controllers
277
+ 4. **Leverage morphing** - For complex updates
278
+ 5. **Progressive enhancement** - Works without JS first
@@ -0,0 +1,334 @@
1
+ # Skill: Internationalization (i18n)
2
+
3
+ ## Purpose
4
+ Implement multi-language support using Rails built-in i18n framework.
5
+
6
+ ## Configuration
7
+
8
+ ### Available Locales
9
+ ```ruby
10
+ # config/application.rb
11
+ config.i18n.available_locales = [:en, :es, :fr]
12
+ config.i18n.default_locale = :en
13
+ config.i18n.fallbacks = true
14
+ ```
15
+
16
+ ### Load Path
17
+ ```ruby
18
+ # config/application.rb
19
+ config.i18n.load_path += Dir[Rails.root.join("config/locales/**/*.{rb,yml}")]
20
+ ```
21
+
22
+ ## Locale Files Structure
23
+
24
+ ```
25
+ config/locales/
26
+ ├── en.yml # Default English
27
+ ├── es.yml # Spanish
28
+ ├── models/
29
+ │ ├── user.en.yml
30
+ │ └── user.es.yml
31
+ ├── views/
32
+ │ ├── articles.en.yml
33
+ │ └── articles.es.yml
34
+ └── defaults/
35
+ ├── en.yml
36
+ └── es.yml
37
+ ```
38
+
39
+ ## Basic Translations
40
+
41
+ ### Simple Strings
42
+ ```yaml
43
+ # config/locales/en.yml
44
+ en:
45
+ hello: "Hello"
46
+ welcome: "Welcome to our app"
47
+
48
+ # config/locales/es.yml
49
+ es:
50
+ hello: "Hola"
51
+ welcome: "Bienvenido a nuestra app"
52
+ ```
53
+
54
+ ### Nested Structure
55
+ ```yaml
56
+ # config/locales/en.yml
57
+ en:
58
+ navigation:
59
+ home: "Home"
60
+ about: "About"
61
+ contact: "Contact"
62
+
63
+ footer:
64
+ copyright: "All rights reserved"
65
+ privacy: "Privacy Policy"
66
+ ```
67
+
68
+ ### With Variables
69
+ ```yaml
70
+ en:
71
+ greeting: "Hello, %{name}!"
72
+ items_count: "You have %{count} items"
73
+ ```
74
+
75
+ ```ruby
76
+ t("greeting", name: "John") # => "Hello, John!"
77
+ ```
78
+
79
+ ## Pluralization
80
+
81
+ ```yaml
82
+ en:
83
+ articles:
84
+ count:
85
+ zero: "No articles"
86
+ one: "1 article"
87
+ other: "%{count} articles"
88
+ ```
89
+
90
+ ```ruby
91
+ t("articles.count", count: 0) # => "No articles"
92
+ t("articles.count", count: 1) # => "1 article"
93
+ t("articles.count", count: 5) # => "5 articles"
94
+ ```
95
+
96
+ ## Model Translations
97
+
98
+ ### Active Record Attributes
99
+ ```yaml
100
+ # config/locales/models/article.en.yml
101
+ en:
102
+ activerecord:
103
+ models:
104
+ article:
105
+ one: "Article"
106
+ other: "Articles"
107
+ attributes:
108
+ article:
109
+ title: "Title"
110
+ body: "Content"
111
+ published: "Published"
112
+ created_at: "Created"
113
+ errors:
114
+ models:
115
+ article:
116
+ attributes:
117
+ title:
118
+ blank: "Please provide a title"
119
+ too_short: "Title must be at least %{count} characters"
120
+ ```
121
+
122
+ ### Usage in Views
123
+ ```erb
124
+ <%= Article.model_name.human %>
125
+ <%= Article.human_attribute_name(:title) %>
126
+ ```
127
+
128
+ ## View Translations
129
+
130
+ ### Lazy Lookup
131
+ ```yaml
132
+ # config/locales/views/articles.en.yml
133
+ en:
134
+ articles:
135
+ index:
136
+ title: "All Articles"
137
+ new_article: "New Article"
138
+ no_articles: "No articles yet"
139
+ show:
140
+ edit: "Edit"
141
+ delete: "Delete"
142
+ confirm_delete: "Are you sure?"
143
+ form:
144
+ submit_create: "Create Article"
145
+ submit_update: "Update Article"
146
+ ```
147
+
148
+ ```erb
149
+ <%# app/views/articles/index.html.erb %>
150
+ <h1><%= t(".title") %></h1>
151
+ <%= link_to t(".new_article"), new_article_path %>
152
+
153
+ <% if @articles.empty? %>
154
+ <p><%= t(".no_articles") %></p>
155
+ <% end %>
156
+ ```
157
+
158
+ ### With HTML
159
+ ```yaml
160
+ en:
161
+ welcome_html: "Welcome to <strong>Our App</strong>"
162
+ terms_html: "By signing up, you agree to our %{link}"
163
+ ```
164
+
165
+ ```erb
166
+ <%= t("welcome_html") %>
167
+ <%= t("terms_html", link: link_to(t("terms"), terms_path)) %>
168
+ ```
169
+
170
+ ## Date and Time
171
+
172
+ ### Formats
173
+ ```yaml
174
+ en:
175
+ date:
176
+ formats:
177
+ default: "%Y-%m-%d"
178
+ short: "%b %d"
179
+ long: "%B %d, %Y"
180
+ time:
181
+ formats:
182
+ default: "%Y-%m-%d %H:%M"
183
+ short: "%d %b %H:%M"
184
+ long: "%B %d, %Y at %H:%M"
185
+ ```
186
+
187
+ ### Usage
188
+ ```erb
189
+ <%= l(article.created_at) %>
190
+ <%= l(article.created_at, format: :short) %>
191
+ <%= l(article.created_at, format: :long) %>
192
+ ```
193
+
194
+ ## Controller Setup
195
+
196
+ ### Set Locale
197
+ ```ruby
198
+ # app/controllers/application_controller.rb
199
+ class ApplicationController < ActionController::Base
200
+ around_action :switch_locale
201
+
202
+ private
203
+
204
+ def switch_locale(&action)
205
+ locale = extract_locale || I18n.default_locale
206
+ I18n.with_locale(locale, &action)
207
+ end
208
+
209
+ def extract_locale
210
+ # From URL param
211
+ locale = params[:locale]
212
+ # From user preference
213
+ locale ||= current_user&.locale
214
+ # From Accept-Language header
215
+ locale ||= extract_locale_from_header
216
+
217
+ locale if I18n.available_locales.include?(locale&.to_sym)
218
+ end
219
+
220
+ def extract_locale_from_header
221
+ request.env["HTTP_ACCEPT_LANGUAGE"]&.scan(/^[a-z]{2}/)&.first
222
+ end
223
+
224
+ def default_url_options
225
+ { locale: I18n.locale }
226
+ end
227
+ end
228
+ ```
229
+
230
+ ### URL-Based Locale
231
+ ```ruby
232
+ # config/routes.rb
233
+ Rails.application.routes.draw do
234
+ scope "(:locale)", locale: /en|es|fr/ do
235
+ resources :articles
236
+ root "pages#home"
237
+ end
238
+ end
239
+ ```
240
+
241
+ ## JavaScript Translations
242
+
243
+ ### Export to JS
244
+ ```ruby
245
+ # config/initializers/i18n_js.rb
246
+ # Using i18n-js gem
247
+ I18nJS.configure do |config|
248
+ config.translations do |t|
249
+ t.file = "app/javascript/translations.json"
250
+ t.patterns << "*"
251
+ end
252
+ end
253
+ ```
254
+
255
+ ### Stimulus Controller
256
+ ```javascript
257
+ // app/javascript/controllers/i18n_controller.js
258
+ import { Controller } from "@hotwired/stimulus"
259
+ import translations from "../translations.json"
260
+
261
+ export default class extends Controller {
262
+ static values = { locale: String }
263
+
264
+ t(key, options = {}) {
265
+ let translation = translations[this.localeValue][key]
266
+
267
+ Object.keys(options).forEach(k => {
268
+ translation = translation.replace(`%{${k}}`, options[k])
269
+ })
270
+
271
+ return translation
272
+ }
273
+ }
274
+ ```
275
+
276
+ ## Form Errors
277
+
278
+ ```yaml
279
+ en:
280
+ errors:
281
+ format: "%{attribute} %{message}"
282
+ messages:
283
+ blank: "can't be blank"
284
+ invalid: "is invalid"
285
+ too_short: "is too short (minimum is %{count} characters)"
286
+ too_long: "is too long (maximum is %{count} characters)"
287
+ taken: "has already been taken"
288
+ ```
289
+
290
+ ## Flash Messages
291
+
292
+ ```yaml
293
+ en:
294
+ flash:
295
+ actions:
296
+ create:
297
+ success: "%{model} was successfully created."
298
+ error: "Could not create %{model}."
299
+ update:
300
+ success: "%{model} was successfully updated."
301
+ error: "Could not update %{model}."
302
+ destroy:
303
+ success: "%{model} was successfully deleted."
304
+ ```
305
+
306
+ ```ruby
307
+ # Controller
308
+ redirect_to @article, notice: t("flash.actions.create.success", model: Article.model_name.human)
309
+ ```
310
+
311
+ ## Testing
312
+
313
+ ```ruby
314
+ # spec/support/i18n.rb
315
+ RSpec.configure do |config|
316
+ config.before(:each) do
317
+ I18n.locale = :en
318
+ end
319
+ end
320
+
321
+ # Test missing translations
322
+ I18n.exception_handler = ->(exception, locale, key, options) {
323
+ raise exception.to_exception
324
+ }
325
+ ```
326
+
327
+ ## Best Practices
328
+
329
+ 1. **Use lazy lookup** - `.title` instead of full path
330
+ 2. **Organize by feature** - Separate files per domain
331
+ 3. **Use YAML anchors** - Reduce duplication
332
+ 4. **Test all locales** - Ensure no missing keys
333
+ 5. **Use variables** - Don't concatenate strings
334
+ 6. **Keep defaults in en.yml** - Fallback always works