agent-notes 2.0.4__py3-none-any.whl
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.
- agent_notes/VERSION +1 -0
- agent_notes/__init__.py +1 -0
- agent_notes/__main__.py +4 -0
- agent_notes/cli.py +348 -0
- agent_notes/commands/__init__.py +27 -0
- agent_notes/commands/_install_helpers.py +262 -0
- agent_notes/commands/build.py +170 -0
- agent_notes/commands/doctor.py +112 -0
- agent_notes/commands/info.py +95 -0
- agent_notes/commands/install.py +99 -0
- agent_notes/commands/list.py +169 -0
- agent_notes/commands/memory.py +430 -0
- agent_notes/commands/regenerate.py +152 -0
- agent_notes/commands/set_role.py +143 -0
- agent_notes/commands/uninstall.py +26 -0
- agent_notes/commands/update.py +169 -0
- agent_notes/commands/validate.py +199 -0
- agent_notes/commands/wizard.py +720 -0
- agent_notes/config.py +154 -0
- agent_notes/data/agents/agents.yaml +352 -0
- agent_notes/data/agents/analyst.md +45 -0
- agent_notes/data/agents/api-reviewer.md +47 -0
- agent_notes/data/agents/architect.md +46 -0
- agent_notes/data/agents/coder.md +28 -0
- agent_notes/data/agents/database-specialist.md +45 -0
- agent_notes/data/agents/debugger.md +47 -0
- agent_notes/data/agents/devil.md +47 -0
- agent_notes/data/agents/devops.md +38 -0
- agent_notes/data/agents/explorer.md +23 -0
- agent_notes/data/agents/integrations.md +44 -0
- agent_notes/data/agents/lead.md +216 -0
- agent_notes/data/agents/performance-profiler.md +44 -0
- agent_notes/data/agents/refactorer.md +48 -0
- agent_notes/data/agents/reviewer.md +44 -0
- agent_notes/data/agents/security-auditor.md +44 -0
- agent_notes/data/agents/system-auditor.md +38 -0
- agent_notes/data/agents/tech-writer.md +32 -0
- agent_notes/data/agents/test-runner.md +36 -0
- agent_notes/data/agents/test-writer.md +39 -0
- agent_notes/data/cli/claude.yaml +25 -0
- agent_notes/data/cli/copilot.yaml +18 -0
- agent_notes/data/cli/opencode.yaml +22 -0
- agent_notes/data/commands/brainstorm.md +8 -0
- agent_notes/data/commands/debug.md +9 -0
- agent_notes/data/commands/review.md +10 -0
- agent_notes/data/global-claude.md +290 -0
- agent_notes/data/global-copilot.md +27 -0
- agent_notes/data/global-opencode.md +40 -0
- agent_notes/data/hooks/session-context.md.tpl +19 -0
- agent_notes/data/models/claude-haiku-4-5.yaml +15 -0
- agent_notes/data/models/claude-opus-4-1.yaml +16 -0
- agent_notes/data/models/claude-opus-4-5.yaml +16 -0
- agent_notes/data/models/claude-opus-4-6.yaml +16 -0
- agent_notes/data/models/claude-opus-4-7.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4-5.yaml +16 -0
- agent_notes/data/models/claude-sonnet-4-6.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4.yaml +16 -0
- agent_notes/data/pricing.yaml +33 -0
- agent_notes/data/roles/orchestrator.yaml +5 -0
- agent_notes/data/roles/reasoner.yaml +5 -0
- agent_notes/data/roles/scout.yaml +5 -0
- agent_notes/data/roles/worker.yaml +5 -0
- agent_notes/data/rules/code-quality.md +9 -0
- agent_notes/data/rules/safety.md +10 -0
- agent_notes/data/scripts/cost-report +211 -0
- agent_notes/data/skills/brainstorming/SKILL.md +57 -0
- agent_notes/data/skills/code-review/SKILL.md +64 -0
- agent_notes/data/skills/debugging-protocol/SKILL.md +51 -0
- agent_notes/data/skills/docker-compose/SKILL.md +318 -0
- agent_notes/data/skills/docker-compose-advanced/SKILL.md +575 -0
- agent_notes/data/skills/docker-dockerfile/SKILL.md +385 -0
- agent_notes/data/skills/docker-dockerfile-languages/SKILL.md +293 -0
- agent_notes/data/skills/git/SKILL.md +87 -0
- agent_notes/data/skills/rails-active-storage/SKILL.md +321 -0
- agent_notes/data/skills/rails-broadcasting/SKILL.md +374 -0
- agent_notes/data/skills/rails-concerns/SKILL.md +806 -0
- agent_notes/data/skills/rails-controllers/SKILL.md +510 -0
- agent_notes/data/skills/rails-controllers-advanced/SKILL.md +441 -0
- agent_notes/data/skills/rails-helpers/SKILL.md +677 -0
- agent_notes/data/skills/rails-initializers/SKILL.md +79 -0
- agent_notes/data/skills/rails-javascript/SKILL.md +567 -0
- agent_notes/data/skills/rails-jobs/SKILL.md +700 -0
- agent_notes/data/skills/rails-kamal/SKILL.md +483 -0
- agent_notes/data/skills/rails-lib/SKILL.md +101 -0
- agent_notes/data/skills/rails-mailers/SKILL.md +321 -0
- agent_notes/data/skills/rails-migrations/SKILL.md +268 -0
- agent_notes/data/skills/rails-models/SKILL.md +459 -0
- agent_notes/data/skills/rails-models-advanced/SKILL.md +398 -0
- agent_notes/data/skills/rails-routes/SKILL.md +804 -0
- agent_notes/data/skills/rails-style/SKILL.md +538 -0
- agent_notes/data/skills/rails-testing-controllers/SKILL.md +343 -0
- agent_notes/data/skills/rails-testing-models/SKILL.md +296 -0
- agent_notes/data/skills/rails-testing-system/SKILL.md +375 -0
- agent_notes/data/skills/rails-validations/SKILL.md +108 -0
- agent_notes/data/skills/rails-view-components/SKILL.md +511 -0
- agent_notes/data/skills/rails-view-components-advanced/SKILL.md +376 -0
- agent_notes/data/skills/rails-views/SKILL.md +413 -0
- agent_notes/data/skills/rails-views-advanced/SKILL.md +450 -0
- agent_notes/data/skills/refactoring-protocol/SKILL.md +64 -0
- agent_notes/data/skills/tdd/SKILL.md +57 -0
- agent_notes/data/templates/__init__.py +1 -0
- agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__init__.py +1 -0
- agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/cursor.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/claude.py +44 -0
- agent_notes/data/templates/frontmatter/opencode.py +104 -0
- agent_notes/doctor_checks.py +189 -0
- agent_notes/domain/__init__.py +17 -0
- agent_notes/domain/agent.py +34 -0
- agent_notes/domain/cli_backend.py +40 -0
- agent_notes/domain/diagnostics.py +29 -0
- agent_notes/domain/diff.py +44 -0
- agent_notes/domain/model.py +27 -0
- agent_notes/domain/role.py +13 -0
- agent_notes/domain/rule.py +13 -0
- agent_notes/domain/skill.py +15 -0
- agent_notes/domain/state.py +46 -0
- agent_notes/install_state.py +11 -0
- agent_notes/registries/__init__.py +16 -0
- agent_notes/registries/_base.py +46 -0
- agent_notes/registries/agent_registry.py +107 -0
- agent_notes/registries/cli_registry.py +89 -0
- agent_notes/registries/model_registry.py +85 -0
- agent_notes/registries/role_registry.py +64 -0
- agent_notes/registries/rule_registry.py +80 -0
- agent_notes/registries/skill_registry.py +141 -0
- agent_notes/services/__init__.py +8 -0
- agent_notes/services/diagnostics/__init__.py +47 -0
- agent_notes/services/diagnostics/_checks.py +272 -0
- agent_notes/services/diagnostics/_display.py +346 -0
- agent_notes/services/diagnostics/_fix.py +169 -0
- agent_notes/services/diff.py +349 -0
- agent_notes/services/fs.py +195 -0
- agent_notes/services/install_state_builder.py +210 -0
- agent_notes/services/installer.py +293 -0
- agent_notes/services/memory_backend.py +155 -0
- agent_notes/services/rendering.py +329 -0
- agent_notes/services/session_context.py +23 -0
- agent_notes/services/settings_writer.py +79 -0
- agent_notes/services/state_store.py +249 -0
- agent_notes/services/ui.py +419 -0
- agent_notes/services/user_config.py +62 -0
- agent_notes/services/validation.py +67 -0
- agent_notes/state.py +21 -0
- agent_notes-2.0.4.dist-info/METADATA +14 -0
- agent_notes-2.0.4.dist-info/RECORD +162 -0
- agent_notes-2.0.4.dist-info/WHEEL +5 -0
- agent_notes-2.0.4.dist-info/entry_points.txt +2 -0
- agent_notes-2.0.4.dist-info/licenses/LICENSE +21 -0
- agent_notes-2.0.4.dist-info/top_level.txt +2 -0
- tests/conftest.py +20 -0
- tests/functional/__init__.py +0 -0
- tests/functional/test_build_commands.py +88 -0
- tests/functional/test_registries.py +128 -0
- tests/integration/__init__.py +0 -0
- tests/integration/test_build_output.py +129 -0
- tests/plugins/__init__.py +0 -0
- tests/plugins/test_agents.py +93 -0
- tests/plugins/test_skills.py +77 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-views-advanced
|
|
3
|
+
description: "Rails views advanced: Turbo Streams, JSON/Jbuilder, helpers in views, conditional rendering, and best practices"
|
|
4
|
+
group: rails
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Views (Advanced)
|
|
8
|
+
|
|
9
|
+
## Turbo Streams
|
|
10
|
+
|
|
11
|
+
### Turbo Stream Template
|
|
12
|
+
|
|
13
|
+
```erb
|
|
14
|
+
<%# app/views/cards/create.turbo_stream.erb %>
|
|
15
|
+
|
|
16
|
+
<%# Prepend new card to list %>
|
|
17
|
+
<%= turbo_stream.prepend "cards", @card %>
|
|
18
|
+
|
|
19
|
+
<%# Update form with errors or clear it %>
|
|
20
|
+
<% if @card.persisted? %>
|
|
21
|
+
<%= turbo_stream.update "new_card_form", "" %>
|
|
22
|
+
<% else %>
|
|
23
|
+
<%= turbo_stream.replace "new_card_form" do %>
|
|
24
|
+
<%= render "form", card: @card %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
|
|
28
|
+
<%# Show flash message %>
|
|
29
|
+
<%= turbo_stream.update "flash" do %>
|
|
30
|
+
<div class="alert alert-success">Card created!</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Multiple Turbo Stream Actions
|
|
35
|
+
|
|
36
|
+
```erb
|
|
37
|
+
<%# app/views/cards/update.turbo_stream.erb %>
|
|
38
|
+
|
|
39
|
+
<%= turbo_stream.replace @card, @card %>
|
|
40
|
+
|
|
41
|
+
<%= turbo_stream.update "card_count" do %>
|
|
42
|
+
<%= pluralize(Card.count, "card") %>
|
|
43
|
+
<% end %>
|
|
44
|
+
|
|
45
|
+
<%= turbo_stream.append "recent_activity" do %>
|
|
46
|
+
<%= render "activity", card: @card, action: "updated" %>
|
|
47
|
+
<% end %>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Turbo Stream Actions
|
|
51
|
+
|
|
52
|
+
```erb
|
|
53
|
+
<%# Replace %>
|
|
54
|
+
<%= turbo_stream.replace dom_id(@card), @card %>
|
|
55
|
+
|
|
56
|
+
<%# Update (replace innerHTML) %>
|
|
57
|
+
<%= turbo_stream.update dom_id(@card), partial: "cards/card" %>
|
|
58
|
+
|
|
59
|
+
<%# Append %>
|
|
60
|
+
<%= turbo_stream.append "cards", @card %>
|
|
61
|
+
|
|
62
|
+
<%# Prepend %>
|
|
63
|
+
<%= turbo_stream.prepend "cards", @card %>
|
|
64
|
+
|
|
65
|
+
<%# Remove %>
|
|
66
|
+
<%= turbo_stream.remove @card %>
|
|
67
|
+
|
|
68
|
+
<%# Before %>
|
|
69
|
+
<%= turbo_stream.before dom_id(@card), partial: "cards/notice" %>
|
|
70
|
+
|
|
71
|
+
<%# After %>
|
|
72
|
+
<%= turbo_stream.after dom_id(@card), partial: "cards/metadata" %>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## JSON Views (Jbuilder)
|
|
78
|
+
|
|
79
|
+
### Basic JSON Template
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# app/views/cards/show.json.jbuilder
|
|
83
|
+
|
|
84
|
+
json.id @card.id
|
|
85
|
+
json.title @card.title
|
|
86
|
+
json.description @card.description.to_plain_text
|
|
87
|
+
json.status @card.status
|
|
88
|
+
json.created_at @card.created_at
|
|
89
|
+
json.updated_at @card.updated_at
|
|
90
|
+
|
|
91
|
+
json.url card_url(@card)
|
|
92
|
+
|
|
93
|
+
json.creator do
|
|
94
|
+
json.id @card.creator.id
|
|
95
|
+
json.name @card.creator.name
|
|
96
|
+
json.email @card.creator.email
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
json.tags @card.tags, :id, :title
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### JSON with Partials
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# app/views/cards/show.json.jbuilder
|
|
106
|
+
|
|
107
|
+
json.partial! "cards/card", card: @card
|
|
108
|
+
|
|
109
|
+
json.comments @card.comments do |comment|
|
|
110
|
+
json.partial! "comments/comment", comment: comment
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# app/views/cards/_card.json.jbuilder
|
|
116
|
+
|
|
117
|
+
json.cache! card do
|
|
118
|
+
json.(card, :id, :number, :title, :status)
|
|
119
|
+
json.description card.description.to_plain_text
|
|
120
|
+
json.description_html card.description.to_s
|
|
121
|
+
|
|
122
|
+
json.url card_url(card)
|
|
123
|
+
json.created_at card.created_at.iso8601
|
|
124
|
+
|
|
125
|
+
json.creator do
|
|
126
|
+
json.partial! "users/user", user: card.creator
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### JSON Collection
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# app/views/cards/index.json.jbuilder
|
|
135
|
+
|
|
136
|
+
json.cards @cards do |card|
|
|
137
|
+
json.partial! "cards/card", card: card
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
json.meta do
|
|
141
|
+
json.total_count @cards.total_count
|
|
142
|
+
json.current_page @cards.current_page
|
|
143
|
+
json.total_pages @cards.total_pages
|
|
144
|
+
end
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Helpers in Views
|
|
150
|
+
|
|
151
|
+
### Link Helpers
|
|
152
|
+
|
|
153
|
+
```erb
|
|
154
|
+
<%= link_to "View Card", @card %>
|
|
155
|
+
<%= link_to "Edit", edit_card_path(@card), class: "btn" %>
|
|
156
|
+
<%= link_to "Delete", @card, method: :delete, data: { confirm: "Are you sure?" } %>
|
|
157
|
+
|
|
158
|
+
<%# Turbo-specific %>
|
|
159
|
+
<%= link_to "View", @card, data: { turbo_frame: "modal" } %>
|
|
160
|
+
<%= link_to "Edit", edit_card_path(@card), data: { turbo: false } %>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Button Helpers
|
|
164
|
+
|
|
165
|
+
```erb
|
|
166
|
+
<%= button_to "Delete", @card, method: :delete, class: "btn btn-danger" %>
|
|
167
|
+
<%= button_to "Archive", archive_card_path(@card), method: :post %>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Image Helpers
|
|
171
|
+
|
|
172
|
+
```erb
|
|
173
|
+
<%= image_tag "logo.png", alt: "Logo", class: "logo" %>
|
|
174
|
+
<%= image_tag card.image, size: "300x200" if card.image.attached? %>
|
|
175
|
+
|
|
176
|
+
<%# With Active Storage %>
|
|
177
|
+
<%= image_tag url_for(card.image) if card.image.attached? %>
|
|
178
|
+
<%= image_tag card.image.variant(resize_to_limit: [300, 200]) %>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Content Tag Helpers
|
|
182
|
+
|
|
183
|
+
```erb
|
|
184
|
+
<%= content_tag :div, class: "card" do %>
|
|
185
|
+
<h3><%= @card.title %></h3>
|
|
186
|
+
<% end %>
|
|
187
|
+
|
|
188
|
+
<%= tag.article class: "card", id: dom_id(@card) do %>
|
|
189
|
+
<h3><%= @card.title %></h3>
|
|
190
|
+
<% end %>
|
|
191
|
+
|
|
192
|
+
<%# Self-closing tags %>
|
|
193
|
+
<%= tag.br %>
|
|
194
|
+
<%= tag.hr %>
|
|
195
|
+
<%= tag.img src: "logo.png" %>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Text Helpers
|
|
199
|
+
|
|
200
|
+
```erb
|
|
201
|
+
<%= truncate(@card.description, length: 100) %>
|
|
202
|
+
<%= excerpt(@card.description, "keyword", radius: 50) %>
|
|
203
|
+
<%= highlight(@card.title, "search term") %>
|
|
204
|
+
<%= pluralize(@card.comments.count, "comment") %>
|
|
205
|
+
<%= number_to_currency(100.50) %>
|
|
206
|
+
<%= number_to_percentage(85.5, precision: 1) %>
|
|
207
|
+
<%= number_with_delimiter(1000000) %>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Date/Time Helpers
|
|
211
|
+
|
|
212
|
+
```erb
|
|
213
|
+
<%= time_ago_in_words(@card.created_at) %> ago
|
|
214
|
+
<%= distance_of_time_in_words(@card.created_at, Time.current) %>
|
|
215
|
+
|
|
216
|
+
<%# Formatted %>
|
|
217
|
+
<%= @card.created_at.strftime("%B %d, %Y") %>
|
|
218
|
+
<%= l(@card.created_at, format: :long) %>
|
|
219
|
+
|
|
220
|
+
<%# Time tag %>
|
|
221
|
+
<time datetime="<%= @card.created_at.iso8601 %>">
|
|
222
|
+
<%= @card.created_at.strftime("%b %d, %Y") %>
|
|
223
|
+
</time>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Conditional Rendering
|
|
229
|
+
|
|
230
|
+
### Simple Conditionals
|
|
231
|
+
|
|
232
|
+
```erb
|
|
233
|
+
<% if @card.published? %>
|
|
234
|
+
<span class="badge badge-published">Published</span>
|
|
235
|
+
<% else %>
|
|
236
|
+
<span class="badge badge-draft">Draft</span>
|
|
237
|
+
<% end %>
|
|
238
|
+
|
|
239
|
+
<% unless @card.closed? %>
|
|
240
|
+
<%= link_to "Close", card_closure_path(@card), method: :post %>
|
|
241
|
+
<% end %>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Guard Clauses
|
|
245
|
+
|
|
246
|
+
```erb
|
|
247
|
+
<% if @cards.empty? %>
|
|
248
|
+
<p>No cards found.</p>
|
|
249
|
+
<% else %>
|
|
250
|
+
<%= render @cards %>
|
|
251
|
+
<% end %>
|
|
252
|
+
|
|
253
|
+
<%# With present? %>
|
|
254
|
+
<% if @card.description.present? %>
|
|
255
|
+
<div class="description">
|
|
256
|
+
<%= @card.description %>
|
|
257
|
+
</div>
|
|
258
|
+
<% end %>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Ternary Operator
|
|
262
|
+
|
|
263
|
+
```erb
|
|
264
|
+
<span class="status <%= @card.published? ? "active" : "inactive" %>">
|
|
265
|
+
<%= @card.status %>
|
|
266
|
+
</span>
|
|
267
|
+
|
|
268
|
+
<%= link_to(@card.closed? ? "Reopen" : "Close", card_closure_path(@card)) %>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Iteration
|
|
274
|
+
|
|
275
|
+
### Each Loop
|
|
276
|
+
|
|
277
|
+
```erb
|
|
278
|
+
<% @cards.each do |card| %>
|
|
279
|
+
<%= render card %>
|
|
280
|
+
<% end %>
|
|
281
|
+
|
|
282
|
+
<%# With index %>
|
|
283
|
+
<% @cards.each_with_index do |card, index| %>
|
|
284
|
+
<div class="card" data-index="<%= index %>">
|
|
285
|
+
<%= render card %>
|
|
286
|
+
</div>
|
|
287
|
+
<% end %>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Collection Rendering
|
|
291
|
+
|
|
292
|
+
```erb
|
|
293
|
+
<%# Preferred way - cleaner %>
|
|
294
|
+
<%= render @cards %>
|
|
295
|
+
|
|
296
|
+
<%# Same as: %>
|
|
297
|
+
<% @cards.each do |card| %>
|
|
298
|
+
<%= render "cards/card", card: card %>
|
|
299
|
+
<% end %>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Grouped Collections
|
|
303
|
+
|
|
304
|
+
```erb
|
|
305
|
+
<% @cards.group_by(&:status).each do |status, cards| %>
|
|
306
|
+
<section class="status-group">
|
|
307
|
+
<h2><%= status.humanize %></h2>
|
|
308
|
+
<%= render cards %>
|
|
309
|
+
</section>
|
|
310
|
+
<% end %>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Best Practices
|
|
316
|
+
|
|
317
|
+
### DO
|
|
318
|
+
|
|
319
|
+
1. **Keep views simple** - No business logic
|
|
320
|
+
```erb
|
|
321
|
+
<%# Good %>
|
|
322
|
+
<%= render @card if @card.published? %>
|
|
323
|
+
|
|
324
|
+
<%# Bad %>
|
|
325
|
+
<% if @card.status == "published" && @card.visible_to?(current_user) && @card.approved? %>
|
|
326
|
+
<%= render @card %>
|
|
327
|
+
<% end %>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
2. **Use explicit locals in partials**
|
|
331
|
+
```erb
|
|
332
|
+
<%# Good %>
|
|
333
|
+
<%= render "card", card: @card, show_actions: true %>
|
|
334
|
+
|
|
335
|
+
<%# Bad - relies on instance variables %>
|
|
336
|
+
<%= render "card" %>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
3. **Extract complex view logic to helpers**
|
|
340
|
+
```erb
|
|
341
|
+
<%# Good %>
|
|
342
|
+
<%= card_status_badge(@card) %>
|
|
343
|
+
|
|
344
|
+
<%# Bad %>
|
|
345
|
+
<span class="badge badge-<%= @card.status.dasherize %> <%= 'urgent' if @card.urgent? %>">
|
|
346
|
+
<%= @card.status.humanize %>
|
|
347
|
+
</span>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
4. **Use fragment caching**
|
|
351
|
+
```erb
|
|
352
|
+
<% cache @card do %>
|
|
353
|
+
<%= render @card %>
|
|
354
|
+
<% end %>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
5. **Use semantic HTML**
|
|
358
|
+
```erb
|
|
359
|
+
<article class="card">
|
|
360
|
+
<header><h1><%= @card.title %></h1></header>
|
|
361
|
+
<section><%= @card.description %></section>
|
|
362
|
+
<footer><%= render "card/actions" %></footer>
|
|
363
|
+
</article>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### DON'T
|
|
367
|
+
|
|
368
|
+
1. **Database queries in views**
|
|
369
|
+
```erb
|
|
370
|
+
<%# Bad %>
|
|
371
|
+
<% Card.where(status: :published).each do |card| %>
|
|
372
|
+
<%= render card %>
|
|
373
|
+
<% end %>
|
|
374
|
+
|
|
375
|
+
<%# Good - query in controller %>
|
|
376
|
+
<%= render @published_cards %>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
2. **Complex Ruby logic**
|
|
380
|
+
```erb
|
|
381
|
+
<%# Bad %>
|
|
382
|
+
<% total = @cards.inject(0) { |sum, card| sum + card.value } %>
|
|
383
|
+
|
|
384
|
+
<%# Good - method in model/helper %>
|
|
385
|
+
<%= @cards.total_value %>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
3. **Inline styles**
|
|
389
|
+
```erb
|
|
390
|
+
<%# Bad %>
|
|
391
|
+
<div style="color: red; font-size: 14px;">
|
|
392
|
+
|
|
393
|
+
<%# Good %>
|
|
394
|
+
<div class="error-message">
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
4. **Raw HTML without sanitization**
|
|
398
|
+
```erb
|
|
399
|
+
<%# Bad %>
|
|
400
|
+
<%= @card.description.html_safe %>
|
|
401
|
+
|
|
402
|
+
<%# Good %>
|
|
403
|
+
<%= sanitize @card.description %>
|
|
404
|
+
<%= @card.description %> <%# If it's already safe from model %>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Testing Views
|
|
410
|
+
|
|
411
|
+
### View Tests (Helper Tests)
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
# test/helpers/cards_helper_test.rb
|
|
415
|
+
class CardsHelperTest < ActionView::TestCase
|
|
416
|
+
test "card_status_badge returns correct badge" do
|
|
417
|
+
card = cards(:published)
|
|
418
|
+
|
|
419
|
+
badge = card_status_badge(card)
|
|
420
|
+
|
|
421
|
+
assert_match /badge/, badge
|
|
422
|
+
assert_match /published/, badge
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Testing Partials
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
# In controller tests, partials are rendered
|
|
431
|
+
test "index renders cards" do
|
|
432
|
+
get cards_path
|
|
433
|
+
|
|
434
|
+
assert_select ".card", count: Card.count
|
|
435
|
+
assert_select "h3", text: cards(:logo).title
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Summary
|
|
442
|
+
|
|
443
|
+
- **Structure**: Layouts, templates, partials, shared
|
|
444
|
+
- **Partials**: Explicit locals, collection rendering, caching
|
|
445
|
+
- **Forms**: Form builders, nested forms, error handling
|
|
446
|
+
- **Turbo Streams**: Real-time updates without full page reload
|
|
447
|
+
- **JSON**: Jbuilder for API responses
|
|
448
|
+
- **Helpers**: Link, button, image, text, date helpers
|
|
449
|
+
- **Best Practices**: Keep views simple, no business logic, use helpers
|
|
450
|
+
- **Caching**: Fragment caching for performance
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refactoring-protocol
|
|
3
|
+
description: "Safe refactoring: green tests first, one extraction at a time, structure OR behavior never both"
|
|
4
|
+
group: process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Refactoring Protocol
|
|
8
|
+
|
|
9
|
+
Refactoring means improving the structure of code without changing its behavior. The moment you change behavior, you are no longer refactoring — you are adding a feature or fixing a bug. These are different activities and must not be mixed.
|
|
10
|
+
|
|
11
|
+
## Prerequisites — before you start
|
|
12
|
+
|
|
13
|
+
1. **Tests must be green.** If the code has no tests, write characterization tests first that document what it currently does (not what it should do). Do not refactor untested code.
|
|
14
|
+
2. **Understand what you're changing.** Read the code and identify exactly which smell you're addressing.
|
|
15
|
+
3. **One session, one concern.** Name the refactor before starting: "extract payment logic into a service", "rename variables to match domain language", "remove duplication in validation". If you find yourself listing more than one, finish one, commit, then start the next.
|
|
16
|
+
|
|
17
|
+
## Code smells worth addressing
|
|
18
|
+
|
|
19
|
+
- **Long method** — does more than one thing; split by responsibility.
|
|
20
|
+
- **Duplicated logic** — same decision made in multiple places; extract to one location.
|
|
21
|
+
- **Misleading names** — name describes something other than what the code does.
|
|
22
|
+
- **Deep nesting** — more than two levels of if/for; extract guard clauses or methods.
|
|
23
|
+
- **Large class** — handles multiple concerns; split by cohesion.
|
|
24
|
+
- **Primitive obsession** — using raw strings or ints for domain concepts; introduce a value object.
|
|
25
|
+
- **Feature envy** — a method that uses more of another class than its own; move it.
|
|
26
|
+
|
|
27
|
+
## Process
|
|
28
|
+
|
|
29
|
+
### Step 1 — Run tests (confirm green)
|
|
30
|
+
|
|
31
|
+
If any test is failing before you start: stop. Do not refactor broken code.
|
|
32
|
+
|
|
33
|
+
### Step 2 — Make one extraction
|
|
34
|
+
|
|
35
|
+
Extract one method, rename one variable, move one class. Not all at once.
|
|
36
|
+
|
|
37
|
+
### Step 3 — Run tests immediately
|
|
38
|
+
|
|
39
|
+
Every extraction gets a test run. If tests go red:
|
|
40
|
+
- Undo the extraction. Do not try to fix it forward.
|
|
41
|
+
- Understand why it broke before attempting again.
|
|
42
|
+
|
|
43
|
+
### Step 4 — Commit
|
|
44
|
+
|
|
45
|
+
Each logical extraction is its own commit. This makes it reversible and reviewable independently.
|
|
46
|
+
|
|
47
|
+
### Step 5 — Repeat
|
|
48
|
+
|
|
49
|
+
Return to Step 2 for the next extraction.
|
|
50
|
+
|
|
51
|
+
## Hard rules
|
|
52
|
+
|
|
53
|
+
- **Structure OR behavior in one commit, never both.** If you find a bug while refactoring: stash the refactor, fix the bug in a separate commit, then resume. Mixing them makes the change impossible to review and risky to revert.
|
|
54
|
+
- **Do not optimize during refactor.** Performance tuning is a separate session with its own measurement baseline.
|
|
55
|
+
- **Do not add features during refactor.** If you notice a missing edge case: note it, address it in a separate commit after the refactor is complete.
|
|
56
|
+
- **Stop when the smell is gone.** Over-refactoring is as harmful as under-refactoring.
|
|
57
|
+
|
|
58
|
+
## Done means
|
|
59
|
+
|
|
60
|
+
- Tests still green.
|
|
61
|
+
- One code smell addressed.
|
|
62
|
+
- Each logical change committed separately.
|
|
63
|
+
- No behavior was changed.
|
|
64
|
+
- No new features or bug fixes were mixed in.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: "RED-GREEN-REFACTOR: write the failing test first, then make it pass, then clean up"
|
|
4
|
+
group: process
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Test-Driven Development
|
|
8
|
+
|
|
9
|
+
## The contract
|
|
10
|
+
|
|
11
|
+
1. No production code before a failing test.
|
|
12
|
+
2. The minimum code to pass the test — nothing more.
|
|
13
|
+
3. Refactor only while tests are green.
|
|
14
|
+
|
|
15
|
+
## RED — write a failing test
|
|
16
|
+
|
|
17
|
+
- Identify the smallest behavior to verify.
|
|
18
|
+
- Name the test as a specification: `test_returns_empty_list_when_no_results`, not `test_search`.
|
|
19
|
+
- Run it. Confirm it fails for the **right reason** — the assertion fails, not a syntax error or import problem.
|
|
20
|
+
- Do not write production code yet.
|
|
21
|
+
|
|
22
|
+
## GREEN — make it pass
|
|
23
|
+
|
|
24
|
+
- Write the minimum code to pass the test. Hardcode values if that's all it takes — you'll triangulate with the next test.
|
|
25
|
+
- Run the test. Confirm green.
|
|
26
|
+
- If still failing: read the failure output carefully before changing anything else.
|
|
27
|
+
|
|
28
|
+
## REFACTOR — clean up
|
|
29
|
+
|
|
30
|
+
- Eliminate duplication.
|
|
31
|
+
- Improve names.
|
|
32
|
+
- Extract where it improves clarity, not just to reduce line count.
|
|
33
|
+
- Run tests after every change. If they go red: undo the refactor, don't push through.
|
|
34
|
+
|
|
35
|
+
## Test scope rules
|
|
36
|
+
|
|
37
|
+
- Test **behavior** from the outside, not implementation details.
|
|
38
|
+
- Do not test private methods directly — test through the public interface.
|
|
39
|
+
- Prefer fewer, meaningful assertions over many trivial ones.
|
|
40
|
+
- If a test requires more than three mocks to set up: it is testing too much. Split the unit.
|
|
41
|
+
|
|
42
|
+
## When NOT to apply
|
|
43
|
+
|
|
44
|
+
- Exploratory spikes where you're learning an unfamiliar API — spike first, then write tests for what you keep.
|
|
45
|
+
- Tests that require so much mocking the mock becomes the thing being tested — write an integration test instead.
|
|
46
|
+
- Throwaway scripts with no expected lifetime.
|
|
47
|
+
|
|
48
|
+
## Flaky tests
|
|
49
|
+
|
|
50
|
+
If a new test passes sometimes and fails other times: stop and fix it before continuing. A flaky test is worse than no test — it trains you to ignore failures.
|
|
51
|
+
|
|
52
|
+
## Done means
|
|
53
|
+
|
|
54
|
+
- All new tests pass.
|
|
55
|
+
- No existing tests regressed.
|
|
56
|
+
- Test names describe the behavior they verify.
|
|
57
|
+
- No dead code or commented-out experiments remain.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Templates package for agent-notes."""
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Frontmatter template plugins for agent-notes."""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Claude Code frontmatter generator template."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def render(ctx: dict) -> str:
|
|
7
|
+
"""Render YAML frontmatter for a Claude Code agent.
|
|
8
|
+
|
|
9
|
+
ctx keys:
|
|
10
|
+
agent_name: str
|
|
11
|
+
agent_config: dict (from agents.yaml entry)
|
|
12
|
+
model_str: str (resolved model string)
|
|
13
|
+
backend_name: str
|
|
14
|
+
backend: CLIBackend object or None
|
|
15
|
+
"""
|
|
16
|
+
agent_name = ctx['agent_name']
|
|
17
|
+
agent_config = ctx['agent_config']
|
|
18
|
+
model_str = ctx['model_str']
|
|
19
|
+
|
|
20
|
+
frontmatter = ['---']
|
|
21
|
+
frontmatter.append(f'name: {agent_name}')
|
|
22
|
+
frontmatter.append(f'description: {agent_config["description"]}')
|
|
23
|
+
frontmatter.append(f'model: {model_str}')
|
|
24
|
+
|
|
25
|
+
# Add Claude-specific settings
|
|
26
|
+
claude_config = agent_config.get('claude', {})
|
|
27
|
+
if 'tools' in claude_config:
|
|
28
|
+
frontmatter.append(f'tools: {claude_config["tools"]}')
|
|
29
|
+
if 'disallowedTools' in claude_config:
|
|
30
|
+
frontmatter.append(f'disallowedTools: {claude_config["disallowedTools"]}')
|
|
31
|
+
if 'memory' in claude_config:
|
|
32
|
+
frontmatter.append(f'memory: {claude_config["memory"]}')
|
|
33
|
+
|
|
34
|
+
# Add metadata
|
|
35
|
+
frontmatter.append(f'color: {agent_config["color"]}')
|
|
36
|
+
frontmatter.append(f'effort: {agent_config["effort"]}')
|
|
37
|
+
frontmatter.append('---')
|
|
38
|
+
|
|
39
|
+
return '\n'.join(frontmatter)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def post_process(prompt: str, ctx: dict) -> str:
|
|
43
|
+
"""Optional: transform the prompt body. Return as-is by default."""
|
|
44
|
+
return prompt
|