tina4ruby 3.11.14 → 3.11.15
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 +4 -4
- data/CHANGELOG.md +80 -80
- data/LICENSE.txt +21 -21
- data/README.md +137 -137
- data/exe/tina4ruby +5 -5
- data/lib/tina4/ai.rb +696 -696
- data/lib/tina4/api.rb +189 -189
- data/lib/tina4/auth.rb +305 -305
- data/lib/tina4/auto_crud.rb +244 -244
- data/lib/tina4/cache.rb +154 -154
- data/lib/tina4/cli.rb +1449 -1449
- data/lib/tina4/constants.rb +46 -46
- data/lib/tina4/container.rb +74 -74
- data/lib/tina4/cors.rb +74 -74
- data/lib/tina4/crud.rb +692 -692
- data/lib/tina4/database/sqlite3_adapter.rb +165 -165
- data/lib/tina4/database.rb +625 -625
- data/lib/tina4/database_result.rb +208 -208
- data/lib/tina4/debug.rb +8 -8
- data/lib/tina4/dev.rb +14 -14
- data/lib/tina4/dev_admin.rb +935 -935
- data/lib/tina4/dev_mailbox.rb +191 -191
- data/lib/tina4/drivers/firebird_driver.rb +124 -124
- data/lib/tina4/drivers/mongodb_driver.rb +561 -561
- data/lib/tina4/drivers/mssql_driver.rb +112 -112
- data/lib/tina4/drivers/mysql_driver.rb +90 -90
- data/lib/tina4/drivers/odbc_driver.rb +191 -191
- data/lib/tina4/drivers/postgres_driver.rb +116 -116
- data/lib/tina4/drivers/sqlite_driver.rb +122 -122
- data/lib/tina4/env.rb +95 -95
- data/lib/tina4/error_overlay.rb +252 -252
- data/lib/tina4/events.rb +109 -109
- data/lib/tina4/field_types.rb +154 -154
- data/lib/tina4/frond.rb +2025 -2025
- data/lib/tina4/gallery/auth/meta.json +1 -1
- data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -114
- data/lib/tina4/gallery/database/meta.json +1 -1
- data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -43
- data/lib/tina4/gallery/error-overlay/meta.json +1 -1
- data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -17
- data/lib/tina4/gallery/orm/meta.json +1 -1
- data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -16
- data/lib/tina4/gallery/queue/meta.json +1 -1
- data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -325
- data/lib/tina4/gallery/rest-api/meta.json +1 -1
- data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -14
- data/lib/tina4/gallery/templates/meta.json +1 -1
- data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -12
- data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -257
- data/lib/tina4/graphql.rb +966 -966
- data/lib/tina4/health.rb +39 -39
- data/lib/tina4/html_element.rb +170 -170
- data/lib/tina4/job.rb +80 -80
- data/lib/tina4/localization.rb +168 -168
- data/lib/tina4/log.rb +203 -203
- data/lib/tina4/mcp.rb +696 -696
- data/lib/tina4/messenger.rb +587 -587
- data/lib/tina4/metrics.rb +793 -793
- data/lib/tina4/middleware.rb +445 -445
- data/lib/tina4/migration.rb +451 -451
- data/lib/tina4/orm.rb +790 -790
- data/lib/tina4/public/css/tina4.css +2463 -2463
- data/lib/tina4/public/css/tina4.min.css +1 -1
- data/lib/tina4/public/images/logo.svg +5 -5
- data/lib/tina4/public/js/frond.min.js +2 -2
- data/lib/tina4/public/js/tina4-dev-admin.js +565 -565
- data/lib/tina4/public/js/tina4-dev-admin.min.js +480 -480
- data/lib/tina4/public/js/tina4.min.js +92 -92
- data/lib/tina4/public/js/tina4js.min.js +48 -48
- data/lib/tina4/public/swagger/index.html +90 -90
- data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
- data/lib/tina4/query_builder.rb +380 -380
- data/lib/tina4/queue.rb +366 -366
- data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
- data/lib/tina4/queue_backends/lite_backend.rb +298 -298
- data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
- data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
- data/lib/tina4/rack_app.rb +817 -817
- data/lib/tina4/rate_limiter.rb +130 -130
- data/lib/tina4/request.rb +268 -255
- data/lib/tina4/response.rb +346 -346
- data/lib/tina4/response_cache.rb +551 -551
- data/lib/tina4/router.rb +406 -406
- data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
- data/lib/tina4/scss/tina4css/_badges.scss +22 -22
- data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
- data/lib/tina4/scss/tina4css/_cards.scss +49 -49
- data/lib/tina4/scss/tina4css/_forms.scss +156 -156
- data/lib/tina4/scss/tina4css/_grid.scss +81 -81
- data/lib/tina4/scss/tina4css/_modals.scss +84 -84
- data/lib/tina4/scss/tina4css/_nav.scss +149 -149
- data/lib/tina4/scss/tina4css/_reset.scss +94 -94
- data/lib/tina4/scss/tina4css/_tables.scss +54 -54
- data/lib/tina4/scss/tina4css/_typography.scss +55 -55
- data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
- data/lib/tina4/scss/tina4css/_variables.scss +117 -117
- data/lib/tina4/scss/tina4css/base.scss +1 -1
- data/lib/tina4/scss/tina4css/colors.scss +48 -48
- data/lib/tina4/scss/tina4css/tina4.scss +17 -17
- data/lib/tina4/scss_compiler.rb +178 -178
- data/lib/tina4/seeder.rb +567 -567
- data/lib/tina4/service_runner.rb +303 -303
- data/lib/tina4/session.rb +297 -297
- data/lib/tina4/session_handlers/database_handler.rb +72 -72
- data/lib/tina4/session_handlers/file_handler.rb +67 -67
- data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
- data/lib/tina4/session_handlers/redis_handler.rb +43 -43
- data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
- data/lib/tina4/shutdown.rb +84 -84
- data/lib/tina4/sql_translation.rb +158 -158
- data/lib/tina4/swagger.rb +124 -124
- data/lib/tina4/template.rb +894 -894
- data/lib/tina4/templates/base.twig +26 -26
- data/lib/tina4/templates/errors/302.twig +14 -14
- data/lib/tina4/templates/errors/401.twig +9 -9
- data/lib/tina4/templates/errors/403.twig +29 -29
- data/lib/tina4/templates/errors/404.twig +29 -29
- data/lib/tina4/templates/errors/500.twig +38 -38
- data/lib/tina4/templates/errors/502.twig +9 -9
- data/lib/tina4/templates/errors/503.twig +12 -12
- data/lib/tina4/templates/errors/base.twig +37 -37
- data/lib/tina4/test_client.rb +159 -159
- data/lib/tina4/testing.rb +340 -340
- data/lib/tina4/validator.rb +174 -174
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +312 -312
- data/lib/tina4/websocket.rb +343 -343
- data/lib/tina4/websocket_backplane.rb +190 -190
- data/lib/tina4/wsdl.rb +564 -564
- data/lib/tina4.rb +458 -458
- data/lib/tina4ruby.rb +4 -4
- metadata +2 -2
data/lib/tina4/ai.rb
CHANGED
|
@@ -1,696 +1,696 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "fileutils"
|
|
4
|
-
|
|
5
|
-
module Tina4
|
|
6
|
-
# Tina4 AI -- Install AI coding assistant context files.
|
|
7
|
-
#
|
|
8
|
-
# Simple menu-driven installer for AI tool context files.
|
|
9
|
-
# The user picks which tools they use, we install the appropriate files.
|
|
10
|
-
#
|
|
11
|
-
# selection = Tina4::AI.show_menu(".")
|
|
12
|
-
# Tina4::AI.install_selected(".", selection)
|
|
13
|
-
#
|
|
14
|
-
module AI
|
|
15
|
-
# Ordered list of supported AI tools
|
|
16
|
-
AI_TOOLS = [
|
|
17
|
-
{ name: "claude-code", description: "Claude Code", context_file: "CLAUDE.md", config_dir: ".claude" },
|
|
18
|
-
{ name: "cursor", description: "Cursor", context_file: ".cursorules", config_dir: ".cursor" },
|
|
19
|
-
{ name: "copilot", description: "GitHub Copilot", context_file: ".github/copilot-instructions.md", config_dir: ".github" },
|
|
20
|
-
{ name: "windsurf", description: "Windsurf", context_file: ".windsurfrules", config_dir: nil },
|
|
21
|
-
{ name: "aider", description: "Aider", context_file: "CONVENTIONS.md", config_dir: nil },
|
|
22
|
-
{ name: "cline", description: "Cline", context_file: ".clinerules", config_dir: nil },
|
|
23
|
-
{ name: "codex", description: "OpenAI Codex", context_file: "AGENTS.md", config_dir: nil }
|
|
24
|
-
].freeze
|
|
25
|
-
|
|
26
|
-
class << self
|
|
27
|
-
# Check if a tool's context file already exists.
|
|
28
|
-
#
|
|
29
|
-
# @param root [String] project root directory
|
|
30
|
-
# @param tool [Hash] tool entry from AI_TOOLS
|
|
31
|
-
# @return [Boolean]
|
|
32
|
-
def is_installed(root, tool)
|
|
33
|
-
File.exist?(File.join(File.expand_path(root), tool[:context_file]))
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Print the numbered menu and return user input.
|
|
37
|
-
#
|
|
38
|
-
# @param root [String] project root directory (default: ".")
|
|
39
|
-
# @return [String] user input (comma-separated numbers or "all")
|
|
40
|
-
def show_menu(root = ".")
|
|
41
|
-
root = File.expand_path(root)
|
|
42
|
-
green = "\e[32m"
|
|
43
|
-
reset = "\e[0m"
|
|
44
|
-
|
|
45
|
-
puts "\n Tina4 AI Context Installer\n"
|
|
46
|
-
AI_TOOLS.each_with_index do |tool, i|
|
|
47
|
-
marker = is_installed(root, tool) ? " #{green}[installed]#{reset}" : ""
|
|
48
|
-
puts format(" %d. %-20s %s%s", i + 1, tool[:description], tool[:context_file], marker)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# tina4-ai tools option
|
|
52
|
-
tina4_ai_installed = system("which mdview > /dev/null 2>&1")
|
|
53
|
-
marker = tina4_ai_installed ? " #{green}[installed]#{reset}" : ""
|
|
54
|
-
puts " 8. Install tina4-ai tools (requires Python)#{marker}"
|
|
55
|
-
puts
|
|
56
|
-
|
|
57
|
-
print " Select (comma-separated, or 'all'): "
|
|
58
|
-
$stdin.gets&.strip || ""
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Install context files for the selected tools.
|
|
62
|
-
#
|
|
63
|
-
# @param root [String] project root directory
|
|
64
|
-
# @param selection [String] comma-separated numbers like "1,2,3" or "all"
|
|
65
|
-
# @return [Array<String>] list of created/updated file paths
|
|
66
|
-
def install_selected(root, selection)
|
|
67
|
-
root_path = File.expand_path(root)
|
|
68
|
-
created = []
|
|
69
|
-
|
|
70
|
-
if selection.downcase == "all"
|
|
71
|
-
indices = (0...AI_TOOLS.length).to_a
|
|
72
|
-
do_tina4_ai = true
|
|
73
|
-
else
|
|
74
|
-
parts = selection.split(",").map(&:strip).reject(&:empty?)
|
|
75
|
-
indices = []
|
|
76
|
-
do_tina4_ai = false
|
|
77
|
-
parts.each do |p|
|
|
78
|
-
n = Integer(p) rescue next
|
|
79
|
-
if n == 8
|
|
80
|
-
do_tina4_ai = true
|
|
81
|
-
elsif n >= 1 && n <= AI_TOOLS.length
|
|
82
|
-
indices << (n - 1)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
indices.each do |idx|
|
|
88
|
-
tool = AI_TOOLS[idx]
|
|
89
|
-
context = generate_context(tool[:name])
|
|
90
|
-
files = install_for_tool(root_path, tool, context)
|
|
91
|
-
created.concat(files)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
install_tina4_ai if do_tina4_ai
|
|
95
|
-
|
|
96
|
-
created
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Install context for all AI tools (non-interactive).
|
|
100
|
-
#
|
|
101
|
-
# @param root [String] project root directory
|
|
102
|
-
# @return [Array<String>] list of created/updated file paths
|
|
103
|
-
def install_all(root = ".")
|
|
104
|
-
install_selected(root, "all")
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Generate per-tool Tina4 Ruby context document.
|
|
108
|
-
#
|
|
109
|
-
# @param tool_name [String] AI tool name (default: "claude-code")
|
|
110
|
-
# @return [String]
|
|
111
|
-
def generate_context(tool_name = "claude-code")
|
|
112
|
-
case tool_name
|
|
113
|
-
when "claude-code"
|
|
114
|
-
generate_claude_code_context
|
|
115
|
-
when "cursor"
|
|
116
|
-
generate_cursor_context
|
|
117
|
-
when "copilot"
|
|
118
|
-
generate_copilot_context
|
|
119
|
-
when "windsurf"
|
|
120
|
-
generate_windsurf_context
|
|
121
|
-
when "aider"
|
|
122
|
-
generate_aider_context
|
|
123
|
-
when "cline"
|
|
124
|
-
generate_cline_context
|
|
125
|
-
when "codex"
|
|
126
|
-
generate_codex_context
|
|
127
|
-
else
|
|
128
|
-
generate_claude_code_context
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Install context file for a single tool.
|
|
133
|
-
#
|
|
134
|
-
# @param root [String] absolute project root path
|
|
135
|
-
# @param tool [Hash] tool entry from AI_TOOLS
|
|
136
|
-
# @param context [String] generated context content
|
|
137
|
-
# @return [Array<String>] list of created/updated relative file paths
|
|
138
|
-
def install_for_tool(root, tool, context)
|
|
139
|
-
created = []
|
|
140
|
-
context_path = File.join(root, tool[:context_file])
|
|
141
|
-
|
|
142
|
-
# Create directories
|
|
143
|
-
if tool[:config_dir]
|
|
144
|
-
FileUtils.mkdir_p(File.join(root, tool[:config_dir]))
|
|
145
|
-
end
|
|
146
|
-
FileUtils.mkdir_p(File.dirname(context_path))
|
|
147
|
-
|
|
148
|
-
# Always overwrite -- user chose to install
|
|
149
|
-
action = File.exist?(context_path) ? "Updated" : "Installed"
|
|
150
|
-
File.write(context_path, context)
|
|
151
|
-
rel = context_path.sub("#{root}/", "")
|
|
152
|
-
created << rel
|
|
153
|
-
puts " \e[32m✓\e[0m #{action} #{rel}"
|
|
154
|
-
|
|
155
|
-
# Claude-specific extras
|
|
156
|
-
if tool[:name] == "claude-code"
|
|
157
|
-
skills = install_claude_skills(root)
|
|
158
|
-
created.concat(skills)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
created
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Copy Claude Code skill files from the framework's templates.
|
|
165
|
-
#
|
|
166
|
-
# @param root [String] absolute project root path
|
|
167
|
-
# @return [Array<String>] list of created/updated relative file paths
|
|
168
|
-
def install_claude_skills(root)
|
|
169
|
-
created = []
|
|
170
|
-
|
|
171
|
-
# Determine the framework root (where lib/tina4/ lives)
|
|
172
|
-
framework_root = File.expand_path("../../..", __FILE__)
|
|
173
|
-
|
|
174
|
-
# Copy skill directories from .claude/skills/ in the framework to the project
|
|
175
|
-
framework_skills_dir = File.join(framework_root, ".claude", "skills")
|
|
176
|
-
if Dir.exist?(framework_skills_dir)
|
|
177
|
-
target_skills_dir = File.join(root, ".claude", "skills")
|
|
178
|
-
FileUtils.mkdir_p(target_skills_dir)
|
|
179
|
-
Dir.children(framework_skills_dir).each do |entry|
|
|
180
|
-
skill_dir = File.join(framework_skills_dir, entry)
|
|
181
|
-
next unless File.directory?(skill_dir)
|
|
182
|
-
|
|
183
|
-
target_dir = File.join(target_skills_dir, entry)
|
|
184
|
-
FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
|
|
185
|
-
FileUtils.cp_r(skill_dir, target_dir)
|
|
186
|
-
rel = target_dir.sub("#{root}/", "")
|
|
187
|
-
created << rel
|
|
188
|
-
puts " \e[32m✓\e[0m Updated #{rel}"
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# Copy claude-commands if they exist
|
|
193
|
-
commands_source = File.join(framework_root, "templates", "ai", "claude-commands")
|
|
194
|
-
if Dir.exist?(commands_source)
|
|
195
|
-
commands_dir = File.join(root, ".claude", "commands")
|
|
196
|
-
FileUtils.mkdir_p(commands_dir)
|
|
197
|
-
Dir.glob(File.join(commands_source, "*.md")).each do |skill_file|
|
|
198
|
-
target = File.join(commands_dir, File.basename(skill_file))
|
|
199
|
-
FileUtils.cp(skill_file, target)
|
|
200
|
-
rel = target.sub("#{root}/", "")
|
|
201
|
-
created << rel
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
created
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# Install tina4-ai package (provides mdview for markdown viewing).
|
|
209
|
-
def install_tina4_ai
|
|
210
|
-
puts " Installing tina4-ai tools..."
|
|
211
|
-
%w[pip3 pip].each do |cmd|
|
|
212
|
-
next unless system("which #{cmd} > /dev/null 2>&1")
|
|
213
|
-
|
|
214
|
-
result = `#{cmd} install --upgrade tina4-ai 2>&1`
|
|
215
|
-
if $?.success?
|
|
216
|
-
puts " \e[32m✓\e[0m Installed tina4-ai (mdview)"
|
|
217
|
-
return
|
|
218
|
-
else
|
|
219
|
-
puts " \e[33m!\e[0m #{cmd} failed: #{result.strip[0..100]}"
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
puts " \e[33m!\e[0m Python/pip not available -- skip tina4-ai"
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
private
|
|
226
|
-
|
|
227
|
-
# Read existing CLAUDE.md from the framework root.
|
|
228
|
-
#
|
|
229
|
-
# @return [String]
|
|
230
|
-
def generate_claude_code_context
|
|
231
|
-
framework_root = File.expand_path("../../..", __FILE__)
|
|
232
|
-
claude_md = File.join(framework_root, "CLAUDE.md")
|
|
233
|
-
if File.exist?(claude_md)
|
|
234
|
-
File.read(claude_md)
|
|
235
|
-
else
|
|
236
|
-
"# Tina4 Ruby #{Tina4::VERSION}\n\nSee https://tina4.com for documentation.\n"
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Cursor context (~45 lines).
|
|
241
|
-
#
|
|
242
|
-
# @return [String]
|
|
243
|
-
def generate_cursor_context
|
|
244
|
-
<<~CONTEXT
|
|
245
|
-
# Tina4 Ruby #{Tina4::VERSION} — Cursor Rules
|
|
246
|
-
|
|
247
|
-
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
248
|
-
Documentation: https://tina4.com
|
|
249
|
-
|
|
250
|
-
## Project Structure
|
|
251
|
-
|
|
252
|
-
```
|
|
253
|
-
src/routes/ — Route handlers (auto-discovered)
|
|
254
|
-
src/orm/ — ORM models
|
|
255
|
-
src/templates/ — Twig templates
|
|
256
|
-
src/app/ — Service classes
|
|
257
|
-
src/scss/ — SCSS (auto-compiled)
|
|
258
|
-
src/public/ — Static assets
|
|
259
|
-
src/seeds/ — Database seeders
|
|
260
|
-
migrations/ — SQL migration files
|
|
261
|
-
spec/ — RSpec tests
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Route Pattern
|
|
265
|
-
|
|
266
|
-
```ruby
|
|
267
|
-
Tina4.get "/api/users" do |request, response|
|
|
268
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
Tina4.post "/api/users" do |request, response|
|
|
272
|
-
response.call({ created: request.body["name"] }, 201)
|
|
273
|
-
end
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## ORM Pattern
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
class User < Tina4::ORM
|
|
280
|
-
table_name "users"
|
|
281
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
282
|
-
string_field :name, required: true
|
|
283
|
-
string_field :email
|
|
284
|
-
end
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Conventions
|
|
288
|
-
|
|
289
|
-
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
290
|
-
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
291
|
-
3. Every template extends `base.twig`
|
|
292
|
-
4. All schema changes via migrations — never create tables in route code
|
|
293
|
-
5. Use built-in features — never install gems for things Tina4 already provides
|
|
294
|
-
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
295
|
-
7. Use `snake_case` for methods and variables
|
|
296
|
-
|
|
297
|
-
## Built-in Features (No Gems Needed)
|
|
298
|
-
|
|
299
|
-
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
300
|
-
CONTEXT
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
# GitHub Copilot context (~30 lines).
|
|
304
|
-
#
|
|
305
|
-
# @return [String]
|
|
306
|
-
def generate_copilot_context
|
|
307
|
-
<<~CONTEXT
|
|
308
|
-
# Tina4 Ruby #{Tina4::VERSION} — Copilot Instructions
|
|
309
|
-
|
|
310
|
-
This is a **Tina4 Ruby** project. Tina4 is a zero-dependency web framework. Docs: https://tina4.com
|
|
311
|
-
|
|
312
|
-
## Structure
|
|
313
|
-
|
|
314
|
-
Routes in `src/routes/`, ORM models in `src/orm/`, templates in `src/templates/`, services in `src/app/`, tests in `spec/`.
|
|
315
|
-
|
|
316
|
-
## Route Example
|
|
317
|
-
|
|
318
|
-
```ruby
|
|
319
|
-
Tina4.get "/api/users" do |request, response|
|
|
320
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
Tina4.post "/api/users" do |request, response|
|
|
324
|
-
response.call({ created: request.body["name"] }, 201)
|
|
325
|
-
end
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## ORM Example
|
|
329
|
-
|
|
330
|
-
```ruby
|
|
331
|
-
class User < Tina4::ORM
|
|
332
|
-
table_name "users"
|
|
333
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
334
|
-
string_field :name, required: true
|
|
335
|
-
string_field :email
|
|
336
|
-
end
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## Rules
|
|
340
|
-
|
|
341
|
-
- Always return `response.call(data, status)` from routes
|
|
342
|
-
- GET is public; POST/PUT/PATCH/DELETE require auth by default
|
|
343
|
-
- Templates extend `base.twig`; schema changes via migrations only
|
|
344
|
-
- Use `snake_case`; never install gems for built-in features
|
|
345
|
-
- Built-in: Router, ORM, Database, JWT auth, Sessions, GraphQL, WebSocket, Queue, Messenger, Migrations, SCSS, Swagger, i18n, Events, DI, Testing
|
|
346
|
-
CONTEXT
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
# Windsurf context (~60 lines).
|
|
350
|
-
#
|
|
351
|
-
# @return [String]
|
|
352
|
-
def generate_windsurf_context
|
|
353
|
-
<<~CONTEXT
|
|
354
|
-
# Tina4 Ruby #{Tina4::VERSION} — Windsurf Rules
|
|
355
|
-
|
|
356
|
-
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
357
|
-
Documentation: https://tina4.com
|
|
358
|
-
|
|
359
|
-
## Project Structure
|
|
360
|
-
|
|
361
|
-
```
|
|
362
|
-
src/routes/ — Route handlers (auto-discovered)
|
|
363
|
-
src/orm/ — ORM models
|
|
364
|
-
src/templates/ — Twig templates
|
|
365
|
-
src/app/ — Service classes
|
|
366
|
-
src/scss/ — SCSS (auto-compiled)
|
|
367
|
-
src/public/ — Static assets
|
|
368
|
-
src/seeds/ — Database seeders
|
|
369
|
-
migrations/ — SQL migration files
|
|
370
|
-
spec/ — RSpec tests
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
## CLI Commands
|
|
374
|
-
|
|
375
|
-
```bash
|
|
376
|
-
tina4ruby init . # Scaffold project
|
|
377
|
-
tina4ruby serve # Start dev server on port 7147
|
|
378
|
-
tina4ruby migrate # Run database migrations
|
|
379
|
-
tina4ruby test # Run test suite
|
|
380
|
-
tina4ruby routes # List all registered routes
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
## Route Pattern
|
|
384
|
-
|
|
385
|
-
```ruby
|
|
386
|
-
Tina4.get "/api/users" do |request, response|
|
|
387
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
Tina4.post "/api/users" do |request, response|
|
|
391
|
-
response.call({ created: request.body["name"] }, 201)
|
|
392
|
-
end
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## ORM Pattern
|
|
396
|
-
|
|
397
|
-
```ruby
|
|
398
|
-
class User < Tina4::ORM
|
|
399
|
-
table_name "users"
|
|
400
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
401
|
-
string_field :name, required: true
|
|
402
|
-
string_field :email
|
|
403
|
-
end
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
## Template Pattern
|
|
407
|
-
|
|
408
|
-
```twig
|
|
409
|
-
{% extends "base.twig" %}
|
|
410
|
-
{% block content %}
|
|
411
|
-
<div class="container">
|
|
412
|
-
<h1>{{ title }}</h1>
|
|
413
|
-
{% for item in items %}
|
|
414
|
-
<p>{{ item.name }}</p>
|
|
415
|
-
{% endfor %}
|
|
416
|
-
</div>
|
|
417
|
-
{% endblock %}
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
## Conventions
|
|
421
|
-
|
|
422
|
-
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
423
|
-
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
424
|
-
3. Every template extends `base.twig`
|
|
425
|
-
4. All schema changes via migrations — never create tables in route code
|
|
426
|
-
5. Use built-in features — never install gems for things Tina4 already provides
|
|
427
|
-
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
428
|
-
7. Use `snake_case` for methods and variables
|
|
429
|
-
|
|
430
|
-
## Built-in Features (No Gems Needed)
|
|
431
|
-
|
|
432
|
-
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
433
|
-
|
|
434
|
-
## Database Drivers
|
|
435
|
-
|
|
436
|
-
SQLite, PostgreSQL, MySQL, MSSQL, Firebird. Connection string format: `driver://host:port/database`.
|
|
437
|
-
|
|
438
|
-
## Auth
|
|
439
|
-
|
|
440
|
-
JWT auth built-in via `Tina4::Auth`. `secure_get` / `secure_post` for protected routes. Password hashing via `Tina4::Auth.hash_password` / `check_password`.
|
|
441
|
-
CONTEXT
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
# Aider context (~58 lines).
|
|
445
|
-
#
|
|
446
|
-
# @return [String]
|
|
447
|
-
def generate_aider_context
|
|
448
|
-
<<~CONTEXT
|
|
449
|
-
# Tina4 Ruby #{Tina4::VERSION} — Conventions
|
|
450
|
-
|
|
451
|
-
## Framework
|
|
452
|
-
|
|
453
|
-
Tina4 Ruby is a zero-dependency, batteries-included web framework. Docs: https://tina4.com
|
|
454
|
-
|
|
455
|
-
## Project Structure
|
|
456
|
-
|
|
457
|
-
```
|
|
458
|
-
src/routes/ — Route handlers (auto-discovered)
|
|
459
|
-
src/orm/ — ORM models
|
|
460
|
-
src/templates/ — Twig templates
|
|
461
|
-
src/app/ — Service classes
|
|
462
|
-
src/scss/ — SCSS (auto-compiled)
|
|
463
|
-
src/public/ — Static assets
|
|
464
|
-
src/seeds/ — Database seeders
|
|
465
|
-
migrations/ — SQL migration files
|
|
466
|
-
spec/ — RSpec tests
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
## CLI
|
|
470
|
-
|
|
471
|
-
```bash
|
|
472
|
-
tina4ruby init . # Scaffold project
|
|
473
|
-
tina4ruby serve # Start dev server on port 7147
|
|
474
|
-
tina4ruby migrate # Run database migrations
|
|
475
|
-
tina4ruby test # Run test suite
|
|
476
|
-
tina4ruby routes # List all registered routes
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
## Route Pattern
|
|
480
|
-
|
|
481
|
-
```ruby
|
|
482
|
-
Tina4.get "/api/users" do |request, response|
|
|
483
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
Tina4.post "/api/users" do |request, response|
|
|
487
|
-
response.call({ created: request.body["name"] }, 201)
|
|
488
|
-
end
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
## ORM Pattern
|
|
492
|
-
|
|
493
|
-
```ruby
|
|
494
|
-
class User < Tina4::ORM
|
|
495
|
-
table_name "users"
|
|
496
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
497
|
-
string_field :name, required: true
|
|
498
|
-
string_field :email
|
|
499
|
-
end
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
## Conventions
|
|
503
|
-
|
|
504
|
-
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
505
|
-
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
506
|
-
3. Every template extends `base.twig`
|
|
507
|
-
4. All schema changes via migrations — never create tables in route code
|
|
508
|
-
5. Use built-in features — never install gems for things Tina4 already provides
|
|
509
|
-
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
510
|
-
7. Use `snake_case` for methods and variables
|
|
511
|
-
8. Wrap route logic in `begin/rescue`, log with `Tina4::Log.error()`
|
|
512
|
-
|
|
513
|
-
## Built-in Features
|
|
514
|
-
|
|
515
|
-
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
516
|
-
|
|
517
|
-
## Testing
|
|
518
|
-
|
|
519
|
-
Run: `bundle exec rspec` or `tina4ruby test`. Tests in `spec/`.
|
|
520
|
-
CONTEXT
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
# Cline context (~42 lines).
|
|
524
|
-
#
|
|
525
|
-
# @return [String]
|
|
526
|
-
def generate_cline_context
|
|
527
|
-
<<~CONTEXT
|
|
528
|
-
# Tina4 Ruby #{Tina4::VERSION} — Cline Rules
|
|
529
|
-
|
|
530
|
-
Tina4 Ruby is a zero-dependency web framework. Docs: https://tina4.com
|
|
531
|
-
|
|
532
|
-
## Structure
|
|
533
|
-
|
|
534
|
-
```
|
|
535
|
-
src/routes/ — Route handlers (auto-discovered)
|
|
536
|
-
src/orm/ — ORM models
|
|
537
|
-
src/templates/ — Twig templates
|
|
538
|
-
src/app/ — Service classes
|
|
539
|
-
src/scss/ — SCSS (auto-compiled)
|
|
540
|
-
src/public/ — Static assets
|
|
541
|
-
src/seeds/ — Database seeders
|
|
542
|
-
migrations/ — SQL migration files
|
|
543
|
-
spec/ — RSpec tests
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
## Route Pattern
|
|
547
|
-
|
|
548
|
-
```ruby
|
|
549
|
-
Tina4.get "/api/users" do |request, response|
|
|
550
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
Tina4.post "/api/users" do |request, response|
|
|
554
|
-
response.call({ created: request.body["name"] }, 201)
|
|
555
|
-
end
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
## ORM Pattern
|
|
559
|
-
|
|
560
|
-
```ruby
|
|
561
|
-
class User < Tina4::ORM
|
|
562
|
-
table_name "users"
|
|
563
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
564
|
-
string_field :name, required: true
|
|
565
|
-
string_field :email
|
|
566
|
-
end
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
## Conventions
|
|
570
|
-
|
|
571
|
-
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
572
|
-
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
573
|
-
3. Every template extends `base.twig`
|
|
574
|
-
4. All schema changes via migrations — never create tables in route code
|
|
575
|
-
5. Use built-in features — never install gems for things Tina4 already provides
|
|
576
|
-
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
577
|
-
7. Use `snake_case` for methods and variables
|
|
578
|
-
|
|
579
|
-
## Built-in Features
|
|
580
|
-
|
|
581
|
-
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions, GraphQL, WebSocket, Queue, Messenger, Migrations, SCSS, Swagger, i18n, Events, Container/DI, Testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
582
|
-
CONTEXT
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
# OpenAI Codex context (~70 lines).
|
|
586
|
-
#
|
|
587
|
-
# @return [String]
|
|
588
|
-
def generate_codex_context
|
|
589
|
-
<<~CONTEXT
|
|
590
|
-
# Tina4 Ruby #{Tina4::VERSION} — Codex Agent Instructions
|
|
591
|
-
|
|
592
|
-
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
593
|
-
Documentation: https://tina4.com
|
|
594
|
-
|
|
595
|
-
## Project Structure
|
|
596
|
-
|
|
597
|
-
```
|
|
598
|
-
src/routes/ — Route handlers (auto-discovered)
|
|
599
|
-
src/orm/ — ORM models
|
|
600
|
-
src/templates/ — Twig templates
|
|
601
|
-
src/app/ — Service classes
|
|
602
|
-
src/scss/ — SCSS (auto-compiled)
|
|
603
|
-
src/public/ — Static assets
|
|
604
|
-
src/seeds/ — Database seeders
|
|
605
|
-
migrations/ — SQL migration files
|
|
606
|
-
spec/ — RSpec tests
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
## CLI Commands
|
|
610
|
-
|
|
611
|
-
```bash
|
|
612
|
-
tina4ruby init . # Scaffold project
|
|
613
|
-
tina4ruby serve # Start dev server on port 7147
|
|
614
|
-
tina4ruby serve --dev # Dev mode with auto-reload
|
|
615
|
-
tina4ruby migrate # Run database migrations
|
|
616
|
-
tina4ruby test # Run test suite
|
|
617
|
-
tina4ruby routes # List all registered routes
|
|
618
|
-
tina4ruby seed # Run database seeders
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
## Route Pattern
|
|
622
|
-
|
|
623
|
-
```ruby
|
|
624
|
-
Tina4.get "/api/users" do |request, response|
|
|
625
|
-
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
626
|
-
end
|
|
627
|
-
|
|
628
|
-
Tina4.post "/api/users" do |request, response|
|
|
629
|
-
response.call({ created: request.body["name"] }, 201)
|
|
630
|
-
end
|
|
631
|
-
|
|
632
|
-
# Protected GET route
|
|
633
|
-
Tina4.secure_get "/api/admin/users" do |request, response|
|
|
634
|
-
response.call({ users: User.all }, Tina4::HTTP_OK)
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
# Route with template rendering
|
|
638
|
-
Tina4::Router.get "/dashboard", template: "dashboard.twig" do |request, response|
|
|
639
|
-
response.call({ title: "Dashboard" }, Tina4::HTTP_OK)
|
|
640
|
-
end
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
## ORM Pattern
|
|
644
|
-
|
|
645
|
-
```ruby
|
|
646
|
-
class User < Tina4::ORM
|
|
647
|
-
table_name "users"
|
|
648
|
-
integer_field :id, primary_key: true, auto_increment: true
|
|
649
|
-
string_field :name, required: true
|
|
650
|
-
string_field :email
|
|
651
|
-
end
|
|
652
|
-
|
|
653
|
-
# Usage
|
|
654
|
-
user = User.create(name: "Alice", email: "alice@example.com")
|
|
655
|
-
users = User.where("name LIKE ?", ["%ali%"])
|
|
656
|
-
user = User.find(1)
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
## Template Pattern
|
|
660
|
-
|
|
661
|
-
```twig
|
|
662
|
-
{% extends "base.twig" %}
|
|
663
|
-
{% block content %}
|
|
664
|
-
<div class="container">
|
|
665
|
-
<h1>{{ title }}</h1>
|
|
666
|
-
{% for item in items %}
|
|
667
|
-
<p>{{ item.name }}</p>
|
|
668
|
-
{% endfor %}
|
|
669
|
-
</div>
|
|
670
|
-
{% endblock %}
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
## Conventions
|
|
674
|
-
|
|
675
|
-
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
676
|
-
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
677
|
-
3. Every template extends `base.twig`
|
|
678
|
-
4. All schema changes via migrations — never create tables in route code
|
|
679
|
-
5. Use built-in features — never install gems for things Tina4 already provides
|
|
680
|
-
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
681
|
-
7. Use `snake_case` for methods and variables
|
|
682
|
-
8. Wrap route logic in `begin/rescue`, log with `Tina4::Log.error()`
|
|
683
|
-
9. Database drivers: SQLite, PostgreSQL, MySQL, MSSQL, Firebird
|
|
684
|
-
|
|
685
|
-
## Built-in Features (No Gems Needed)
|
|
686
|
-
|
|
687
|
-
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
688
|
-
|
|
689
|
-
## Testing
|
|
690
|
-
|
|
691
|
-
Run: `bundle exec rspec` or `tina4ruby test`. Tests live in `spec/`. Use `Tina4::Testing` for inline tests.
|
|
692
|
-
CONTEXT
|
|
693
|
-
end
|
|
694
|
-
end
|
|
695
|
-
end
|
|
696
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Tina4
|
|
6
|
+
# Tina4 AI -- Install AI coding assistant context files.
|
|
7
|
+
#
|
|
8
|
+
# Simple menu-driven installer for AI tool context files.
|
|
9
|
+
# The user picks which tools they use, we install the appropriate files.
|
|
10
|
+
#
|
|
11
|
+
# selection = Tina4::AI.show_menu(".")
|
|
12
|
+
# Tina4::AI.install_selected(".", selection)
|
|
13
|
+
#
|
|
14
|
+
module AI
|
|
15
|
+
# Ordered list of supported AI tools
|
|
16
|
+
AI_TOOLS = [
|
|
17
|
+
{ name: "claude-code", description: "Claude Code", context_file: "CLAUDE.md", config_dir: ".claude" },
|
|
18
|
+
{ name: "cursor", description: "Cursor", context_file: ".cursorules", config_dir: ".cursor" },
|
|
19
|
+
{ name: "copilot", description: "GitHub Copilot", context_file: ".github/copilot-instructions.md", config_dir: ".github" },
|
|
20
|
+
{ name: "windsurf", description: "Windsurf", context_file: ".windsurfrules", config_dir: nil },
|
|
21
|
+
{ name: "aider", description: "Aider", context_file: "CONVENTIONS.md", config_dir: nil },
|
|
22
|
+
{ name: "cline", description: "Cline", context_file: ".clinerules", config_dir: nil },
|
|
23
|
+
{ name: "codex", description: "OpenAI Codex", context_file: "AGENTS.md", config_dir: nil }
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# Check if a tool's context file already exists.
|
|
28
|
+
#
|
|
29
|
+
# @param root [String] project root directory
|
|
30
|
+
# @param tool [Hash] tool entry from AI_TOOLS
|
|
31
|
+
# @return [Boolean]
|
|
32
|
+
def is_installed(root, tool)
|
|
33
|
+
File.exist?(File.join(File.expand_path(root), tool[:context_file]))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Print the numbered menu and return user input.
|
|
37
|
+
#
|
|
38
|
+
# @param root [String] project root directory (default: ".")
|
|
39
|
+
# @return [String] user input (comma-separated numbers or "all")
|
|
40
|
+
def show_menu(root = ".")
|
|
41
|
+
root = File.expand_path(root)
|
|
42
|
+
green = "\e[32m"
|
|
43
|
+
reset = "\e[0m"
|
|
44
|
+
|
|
45
|
+
puts "\n Tina4 AI Context Installer\n"
|
|
46
|
+
AI_TOOLS.each_with_index do |tool, i|
|
|
47
|
+
marker = is_installed(root, tool) ? " #{green}[installed]#{reset}" : ""
|
|
48
|
+
puts format(" %d. %-20s %s%s", i + 1, tool[:description], tool[:context_file], marker)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# tina4-ai tools option
|
|
52
|
+
tina4_ai_installed = system("which mdview > /dev/null 2>&1")
|
|
53
|
+
marker = tina4_ai_installed ? " #{green}[installed]#{reset}" : ""
|
|
54
|
+
puts " 8. Install tina4-ai tools (requires Python)#{marker}"
|
|
55
|
+
puts
|
|
56
|
+
|
|
57
|
+
print " Select (comma-separated, or 'all'): "
|
|
58
|
+
$stdin.gets&.strip || ""
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Install context files for the selected tools.
|
|
62
|
+
#
|
|
63
|
+
# @param root [String] project root directory
|
|
64
|
+
# @param selection [String] comma-separated numbers like "1,2,3" or "all"
|
|
65
|
+
# @return [Array<String>] list of created/updated file paths
|
|
66
|
+
def install_selected(root, selection)
|
|
67
|
+
root_path = File.expand_path(root)
|
|
68
|
+
created = []
|
|
69
|
+
|
|
70
|
+
if selection.downcase == "all"
|
|
71
|
+
indices = (0...AI_TOOLS.length).to_a
|
|
72
|
+
do_tina4_ai = true
|
|
73
|
+
else
|
|
74
|
+
parts = selection.split(",").map(&:strip).reject(&:empty?)
|
|
75
|
+
indices = []
|
|
76
|
+
do_tina4_ai = false
|
|
77
|
+
parts.each do |p|
|
|
78
|
+
n = Integer(p) rescue next
|
|
79
|
+
if n == 8
|
|
80
|
+
do_tina4_ai = true
|
|
81
|
+
elsif n >= 1 && n <= AI_TOOLS.length
|
|
82
|
+
indices << (n - 1)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
indices.each do |idx|
|
|
88
|
+
tool = AI_TOOLS[idx]
|
|
89
|
+
context = generate_context(tool[:name])
|
|
90
|
+
files = install_for_tool(root_path, tool, context)
|
|
91
|
+
created.concat(files)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
install_tina4_ai if do_tina4_ai
|
|
95
|
+
|
|
96
|
+
created
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Install context for all AI tools (non-interactive).
|
|
100
|
+
#
|
|
101
|
+
# @param root [String] project root directory
|
|
102
|
+
# @return [Array<String>] list of created/updated file paths
|
|
103
|
+
def install_all(root = ".")
|
|
104
|
+
install_selected(root, "all")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Generate per-tool Tina4 Ruby context document.
|
|
108
|
+
#
|
|
109
|
+
# @param tool_name [String] AI tool name (default: "claude-code")
|
|
110
|
+
# @return [String]
|
|
111
|
+
def generate_context(tool_name = "claude-code")
|
|
112
|
+
case tool_name
|
|
113
|
+
when "claude-code"
|
|
114
|
+
generate_claude_code_context
|
|
115
|
+
when "cursor"
|
|
116
|
+
generate_cursor_context
|
|
117
|
+
when "copilot"
|
|
118
|
+
generate_copilot_context
|
|
119
|
+
when "windsurf"
|
|
120
|
+
generate_windsurf_context
|
|
121
|
+
when "aider"
|
|
122
|
+
generate_aider_context
|
|
123
|
+
when "cline"
|
|
124
|
+
generate_cline_context
|
|
125
|
+
when "codex"
|
|
126
|
+
generate_codex_context
|
|
127
|
+
else
|
|
128
|
+
generate_claude_code_context
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Install context file for a single tool.
|
|
133
|
+
#
|
|
134
|
+
# @param root [String] absolute project root path
|
|
135
|
+
# @param tool [Hash] tool entry from AI_TOOLS
|
|
136
|
+
# @param context [String] generated context content
|
|
137
|
+
# @return [Array<String>] list of created/updated relative file paths
|
|
138
|
+
def install_for_tool(root, tool, context)
|
|
139
|
+
created = []
|
|
140
|
+
context_path = File.join(root, tool[:context_file])
|
|
141
|
+
|
|
142
|
+
# Create directories
|
|
143
|
+
if tool[:config_dir]
|
|
144
|
+
FileUtils.mkdir_p(File.join(root, tool[:config_dir]))
|
|
145
|
+
end
|
|
146
|
+
FileUtils.mkdir_p(File.dirname(context_path))
|
|
147
|
+
|
|
148
|
+
# Always overwrite -- user chose to install
|
|
149
|
+
action = File.exist?(context_path) ? "Updated" : "Installed"
|
|
150
|
+
File.write(context_path, context)
|
|
151
|
+
rel = context_path.sub("#{root}/", "")
|
|
152
|
+
created << rel
|
|
153
|
+
puts " \e[32m✓\e[0m #{action} #{rel}"
|
|
154
|
+
|
|
155
|
+
# Claude-specific extras
|
|
156
|
+
if tool[:name] == "claude-code"
|
|
157
|
+
skills = install_claude_skills(root)
|
|
158
|
+
created.concat(skills)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
created
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Copy Claude Code skill files from the framework's templates.
|
|
165
|
+
#
|
|
166
|
+
# @param root [String] absolute project root path
|
|
167
|
+
# @return [Array<String>] list of created/updated relative file paths
|
|
168
|
+
def install_claude_skills(root)
|
|
169
|
+
created = []
|
|
170
|
+
|
|
171
|
+
# Determine the framework root (where lib/tina4/ lives)
|
|
172
|
+
framework_root = File.expand_path("../../..", __FILE__)
|
|
173
|
+
|
|
174
|
+
# Copy skill directories from .claude/skills/ in the framework to the project
|
|
175
|
+
framework_skills_dir = File.join(framework_root, ".claude", "skills")
|
|
176
|
+
if Dir.exist?(framework_skills_dir)
|
|
177
|
+
target_skills_dir = File.join(root, ".claude", "skills")
|
|
178
|
+
FileUtils.mkdir_p(target_skills_dir)
|
|
179
|
+
Dir.children(framework_skills_dir).each do |entry|
|
|
180
|
+
skill_dir = File.join(framework_skills_dir, entry)
|
|
181
|
+
next unless File.directory?(skill_dir)
|
|
182
|
+
|
|
183
|
+
target_dir = File.join(target_skills_dir, entry)
|
|
184
|
+
FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
|
|
185
|
+
FileUtils.cp_r(skill_dir, target_dir)
|
|
186
|
+
rel = target_dir.sub("#{root}/", "")
|
|
187
|
+
created << rel
|
|
188
|
+
puts " \e[32m✓\e[0m Updated #{rel}"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Copy claude-commands if they exist
|
|
193
|
+
commands_source = File.join(framework_root, "templates", "ai", "claude-commands")
|
|
194
|
+
if Dir.exist?(commands_source)
|
|
195
|
+
commands_dir = File.join(root, ".claude", "commands")
|
|
196
|
+
FileUtils.mkdir_p(commands_dir)
|
|
197
|
+
Dir.glob(File.join(commands_source, "*.md")).each do |skill_file|
|
|
198
|
+
target = File.join(commands_dir, File.basename(skill_file))
|
|
199
|
+
FileUtils.cp(skill_file, target)
|
|
200
|
+
rel = target.sub("#{root}/", "")
|
|
201
|
+
created << rel
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
created
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Install tina4-ai package (provides mdview for markdown viewing).
|
|
209
|
+
def install_tina4_ai
|
|
210
|
+
puts " Installing tina4-ai tools..."
|
|
211
|
+
%w[pip3 pip].each do |cmd|
|
|
212
|
+
next unless system("which #{cmd} > /dev/null 2>&1")
|
|
213
|
+
|
|
214
|
+
result = `#{cmd} install --upgrade tina4-ai 2>&1`
|
|
215
|
+
if $?.success?
|
|
216
|
+
puts " \e[32m✓\e[0m Installed tina4-ai (mdview)"
|
|
217
|
+
return
|
|
218
|
+
else
|
|
219
|
+
puts " \e[33m!\e[0m #{cmd} failed: #{result.strip[0..100]}"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
puts " \e[33m!\e[0m Python/pip not available -- skip tina4-ai"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
private
|
|
226
|
+
|
|
227
|
+
# Read existing CLAUDE.md from the framework root.
|
|
228
|
+
#
|
|
229
|
+
# @return [String]
|
|
230
|
+
def generate_claude_code_context
|
|
231
|
+
framework_root = File.expand_path("../../..", __FILE__)
|
|
232
|
+
claude_md = File.join(framework_root, "CLAUDE.md")
|
|
233
|
+
if File.exist?(claude_md)
|
|
234
|
+
File.read(claude_md)
|
|
235
|
+
else
|
|
236
|
+
"# Tina4 Ruby #{Tina4::VERSION}\n\nSee https://tina4.com for documentation.\n"
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Cursor context (~45 lines).
|
|
241
|
+
#
|
|
242
|
+
# @return [String]
|
|
243
|
+
def generate_cursor_context
|
|
244
|
+
<<~CONTEXT
|
|
245
|
+
# Tina4 Ruby #{Tina4::VERSION} — Cursor Rules
|
|
246
|
+
|
|
247
|
+
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
248
|
+
Documentation: https://tina4.com
|
|
249
|
+
|
|
250
|
+
## Project Structure
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
src/routes/ — Route handlers (auto-discovered)
|
|
254
|
+
src/orm/ — ORM models
|
|
255
|
+
src/templates/ — Twig templates
|
|
256
|
+
src/app/ — Service classes
|
|
257
|
+
src/scss/ — SCSS (auto-compiled)
|
|
258
|
+
src/public/ — Static assets
|
|
259
|
+
src/seeds/ — Database seeders
|
|
260
|
+
migrations/ — SQL migration files
|
|
261
|
+
spec/ — RSpec tests
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Route Pattern
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
Tina4.get "/api/users" do |request, response|
|
|
268
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
Tina4.post "/api/users" do |request, response|
|
|
272
|
+
response.call({ created: request.body["name"] }, 201)
|
|
273
|
+
end
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## ORM Pattern
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
class User < Tina4::ORM
|
|
280
|
+
table_name "users"
|
|
281
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
282
|
+
string_field :name, required: true
|
|
283
|
+
string_field :email
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Conventions
|
|
288
|
+
|
|
289
|
+
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
290
|
+
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
291
|
+
3. Every template extends `base.twig`
|
|
292
|
+
4. All schema changes via migrations — never create tables in route code
|
|
293
|
+
5. Use built-in features — never install gems for things Tina4 already provides
|
|
294
|
+
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
295
|
+
7. Use `snake_case` for methods and variables
|
|
296
|
+
|
|
297
|
+
## Built-in Features (No Gems Needed)
|
|
298
|
+
|
|
299
|
+
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
300
|
+
CONTEXT
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# GitHub Copilot context (~30 lines).
|
|
304
|
+
#
|
|
305
|
+
# @return [String]
|
|
306
|
+
def generate_copilot_context
|
|
307
|
+
<<~CONTEXT
|
|
308
|
+
# Tina4 Ruby #{Tina4::VERSION} — Copilot Instructions
|
|
309
|
+
|
|
310
|
+
This is a **Tina4 Ruby** project. Tina4 is a zero-dependency web framework. Docs: https://tina4.com
|
|
311
|
+
|
|
312
|
+
## Structure
|
|
313
|
+
|
|
314
|
+
Routes in `src/routes/`, ORM models in `src/orm/`, templates in `src/templates/`, services in `src/app/`, tests in `spec/`.
|
|
315
|
+
|
|
316
|
+
## Route Example
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
Tina4.get "/api/users" do |request, response|
|
|
320
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
Tina4.post "/api/users" do |request, response|
|
|
324
|
+
response.call({ created: request.body["name"] }, 201)
|
|
325
|
+
end
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## ORM Example
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
class User < Tina4::ORM
|
|
332
|
+
table_name "users"
|
|
333
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
334
|
+
string_field :name, required: true
|
|
335
|
+
string_field :email
|
|
336
|
+
end
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Rules
|
|
340
|
+
|
|
341
|
+
- Always return `response.call(data, status)` from routes
|
|
342
|
+
- GET is public; POST/PUT/PATCH/DELETE require auth by default
|
|
343
|
+
- Templates extend `base.twig`; schema changes via migrations only
|
|
344
|
+
- Use `snake_case`; never install gems for built-in features
|
|
345
|
+
- Built-in: Router, ORM, Database, JWT auth, Sessions, GraphQL, WebSocket, Queue, Messenger, Migrations, SCSS, Swagger, i18n, Events, DI, Testing
|
|
346
|
+
CONTEXT
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Windsurf context (~60 lines).
|
|
350
|
+
#
|
|
351
|
+
# @return [String]
|
|
352
|
+
def generate_windsurf_context
|
|
353
|
+
<<~CONTEXT
|
|
354
|
+
# Tina4 Ruby #{Tina4::VERSION} — Windsurf Rules
|
|
355
|
+
|
|
356
|
+
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
357
|
+
Documentation: https://tina4.com
|
|
358
|
+
|
|
359
|
+
## Project Structure
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
src/routes/ — Route handlers (auto-discovered)
|
|
363
|
+
src/orm/ — ORM models
|
|
364
|
+
src/templates/ — Twig templates
|
|
365
|
+
src/app/ — Service classes
|
|
366
|
+
src/scss/ — SCSS (auto-compiled)
|
|
367
|
+
src/public/ — Static assets
|
|
368
|
+
src/seeds/ — Database seeders
|
|
369
|
+
migrations/ — SQL migration files
|
|
370
|
+
spec/ — RSpec tests
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## CLI Commands
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
tina4ruby init . # Scaffold project
|
|
377
|
+
tina4ruby serve # Start dev server on port 7147
|
|
378
|
+
tina4ruby migrate # Run database migrations
|
|
379
|
+
tina4ruby test # Run test suite
|
|
380
|
+
tina4ruby routes # List all registered routes
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Route Pattern
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
Tina4.get "/api/users" do |request, response|
|
|
387
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
Tina4.post "/api/users" do |request, response|
|
|
391
|
+
response.call({ created: request.body["name"] }, 201)
|
|
392
|
+
end
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## ORM Pattern
|
|
396
|
+
|
|
397
|
+
```ruby
|
|
398
|
+
class User < Tina4::ORM
|
|
399
|
+
table_name "users"
|
|
400
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
401
|
+
string_field :name, required: true
|
|
402
|
+
string_field :email
|
|
403
|
+
end
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Template Pattern
|
|
407
|
+
|
|
408
|
+
```twig
|
|
409
|
+
{% extends "base.twig" %}
|
|
410
|
+
{% block content %}
|
|
411
|
+
<div class="container">
|
|
412
|
+
<h1>{{ title }}</h1>
|
|
413
|
+
{% for item in items %}
|
|
414
|
+
<p>{{ item.name }}</p>
|
|
415
|
+
{% endfor %}
|
|
416
|
+
</div>
|
|
417
|
+
{% endblock %}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Conventions
|
|
421
|
+
|
|
422
|
+
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
423
|
+
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
424
|
+
3. Every template extends `base.twig`
|
|
425
|
+
4. All schema changes via migrations — never create tables in route code
|
|
426
|
+
5. Use built-in features — never install gems for things Tina4 already provides
|
|
427
|
+
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
428
|
+
7. Use `snake_case` for methods and variables
|
|
429
|
+
|
|
430
|
+
## Built-in Features (No Gems Needed)
|
|
431
|
+
|
|
432
|
+
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
433
|
+
|
|
434
|
+
## Database Drivers
|
|
435
|
+
|
|
436
|
+
SQLite, PostgreSQL, MySQL, MSSQL, Firebird. Connection string format: `driver://host:port/database`.
|
|
437
|
+
|
|
438
|
+
## Auth
|
|
439
|
+
|
|
440
|
+
JWT auth built-in via `Tina4::Auth`. `secure_get` / `secure_post` for protected routes. Password hashing via `Tina4::Auth.hash_password` / `check_password`.
|
|
441
|
+
CONTEXT
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Aider context (~58 lines).
|
|
445
|
+
#
|
|
446
|
+
# @return [String]
|
|
447
|
+
def generate_aider_context
|
|
448
|
+
<<~CONTEXT
|
|
449
|
+
# Tina4 Ruby #{Tina4::VERSION} — Conventions
|
|
450
|
+
|
|
451
|
+
## Framework
|
|
452
|
+
|
|
453
|
+
Tina4 Ruby is a zero-dependency, batteries-included web framework. Docs: https://tina4.com
|
|
454
|
+
|
|
455
|
+
## Project Structure
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
src/routes/ — Route handlers (auto-discovered)
|
|
459
|
+
src/orm/ — ORM models
|
|
460
|
+
src/templates/ — Twig templates
|
|
461
|
+
src/app/ — Service classes
|
|
462
|
+
src/scss/ — SCSS (auto-compiled)
|
|
463
|
+
src/public/ — Static assets
|
|
464
|
+
src/seeds/ — Database seeders
|
|
465
|
+
migrations/ — SQL migration files
|
|
466
|
+
spec/ — RSpec tests
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## CLI
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
tina4ruby init . # Scaffold project
|
|
473
|
+
tina4ruby serve # Start dev server on port 7147
|
|
474
|
+
tina4ruby migrate # Run database migrations
|
|
475
|
+
tina4ruby test # Run test suite
|
|
476
|
+
tina4ruby routes # List all registered routes
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## Route Pattern
|
|
480
|
+
|
|
481
|
+
```ruby
|
|
482
|
+
Tina4.get "/api/users" do |request, response|
|
|
483
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
Tina4.post "/api/users" do |request, response|
|
|
487
|
+
response.call({ created: request.body["name"] }, 201)
|
|
488
|
+
end
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## ORM Pattern
|
|
492
|
+
|
|
493
|
+
```ruby
|
|
494
|
+
class User < Tina4::ORM
|
|
495
|
+
table_name "users"
|
|
496
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
497
|
+
string_field :name, required: true
|
|
498
|
+
string_field :email
|
|
499
|
+
end
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## Conventions
|
|
503
|
+
|
|
504
|
+
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
505
|
+
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
506
|
+
3. Every template extends `base.twig`
|
|
507
|
+
4. All schema changes via migrations — never create tables in route code
|
|
508
|
+
5. Use built-in features — never install gems for things Tina4 already provides
|
|
509
|
+
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
510
|
+
7. Use `snake_case` for methods and variables
|
|
511
|
+
8. Wrap route logic in `begin/rescue`, log with `Tina4::Log.error()`
|
|
512
|
+
|
|
513
|
+
## Built-in Features
|
|
514
|
+
|
|
515
|
+
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
516
|
+
|
|
517
|
+
## Testing
|
|
518
|
+
|
|
519
|
+
Run: `bundle exec rspec` or `tina4ruby test`. Tests in `spec/`.
|
|
520
|
+
CONTEXT
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Cline context (~42 lines).
|
|
524
|
+
#
|
|
525
|
+
# @return [String]
|
|
526
|
+
def generate_cline_context
|
|
527
|
+
<<~CONTEXT
|
|
528
|
+
# Tina4 Ruby #{Tina4::VERSION} — Cline Rules
|
|
529
|
+
|
|
530
|
+
Tina4 Ruby is a zero-dependency web framework. Docs: https://tina4.com
|
|
531
|
+
|
|
532
|
+
## Structure
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
src/routes/ — Route handlers (auto-discovered)
|
|
536
|
+
src/orm/ — ORM models
|
|
537
|
+
src/templates/ — Twig templates
|
|
538
|
+
src/app/ — Service classes
|
|
539
|
+
src/scss/ — SCSS (auto-compiled)
|
|
540
|
+
src/public/ — Static assets
|
|
541
|
+
src/seeds/ — Database seeders
|
|
542
|
+
migrations/ — SQL migration files
|
|
543
|
+
spec/ — RSpec tests
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## Route Pattern
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
Tina4.get "/api/users" do |request, response|
|
|
550
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
Tina4.post "/api/users" do |request, response|
|
|
554
|
+
response.call({ created: request.body["name"] }, 201)
|
|
555
|
+
end
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## ORM Pattern
|
|
559
|
+
|
|
560
|
+
```ruby
|
|
561
|
+
class User < Tina4::ORM
|
|
562
|
+
table_name "users"
|
|
563
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
564
|
+
string_field :name, required: true
|
|
565
|
+
string_field :email
|
|
566
|
+
end
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## Conventions
|
|
570
|
+
|
|
571
|
+
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
572
|
+
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
573
|
+
3. Every template extends `base.twig`
|
|
574
|
+
4. All schema changes via migrations — never create tables in route code
|
|
575
|
+
5. Use built-in features — never install gems for things Tina4 already provides
|
|
576
|
+
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
577
|
+
7. Use `snake_case` for methods and variables
|
|
578
|
+
|
|
579
|
+
## Built-in Features
|
|
580
|
+
|
|
581
|
+
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions, GraphQL, WebSocket, Queue, Messenger, Migrations, SCSS, Swagger, i18n, Events, Container/DI, Testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
582
|
+
CONTEXT
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# OpenAI Codex context (~70 lines).
|
|
586
|
+
#
|
|
587
|
+
# @return [String]
|
|
588
|
+
def generate_codex_context
|
|
589
|
+
<<~CONTEXT
|
|
590
|
+
# Tina4 Ruby #{Tina4::VERSION} — Codex Agent Instructions
|
|
591
|
+
|
|
592
|
+
You are working in a **Tina4 Ruby** project — a zero-dependency, batteries-included web framework.
|
|
593
|
+
Documentation: https://tina4.com
|
|
594
|
+
|
|
595
|
+
## Project Structure
|
|
596
|
+
|
|
597
|
+
```
|
|
598
|
+
src/routes/ — Route handlers (auto-discovered)
|
|
599
|
+
src/orm/ — ORM models
|
|
600
|
+
src/templates/ — Twig templates
|
|
601
|
+
src/app/ — Service classes
|
|
602
|
+
src/scss/ — SCSS (auto-compiled)
|
|
603
|
+
src/public/ — Static assets
|
|
604
|
+
src/seeds/ — Database seeders
|
|
605
|
+
migrations/ — SQL migration files
|
|
606
|
+
spec/ — RSpec tests
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## CLI Commands
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
tina4ruby init . # Scaffold project
|
|
613
|
+
tina4ruby serve # Start dev server on port 7147
|
|
614
|
+
tina4ruby serve --dev # Dev mode with auto-reload
|
|
615
|
+
tina4ruby migrate # Run database migrations
|
|
616
|
+
tina4ruby test # Run test suite
|
|
617
|
+
tina4ruby routes # List all registered routes
|
|
618
|
+
tina4ruby seed # Run database seeders
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
## Route Pattern
|
|
622
|
+
|
|
623
|
+
```ruby
|
|
624
|
+
Tina4.get "/api/users" do |request, response|
|
|
625
|
+
response.call({ users: [] }, Tina4::HTTP_OK)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
Tina4.post "/api/users" do |request, response|
|
|
629
|
+
response.call({ created: request.body["name"] }, 201)
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
# Protected GET route
|
|
633
|
+
Tina4.secure_get "/api/admin/users" do |request, response|
|
|
634
|
+
response.call({ users: User.all }, Tina4::HTTP_OK)
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
# Route with template rendering
|
|
638
|
+
Tina4::Router.get "/dashboard", template: "dashboard.twig" do |request, response|
|
|
639
|
+
response.call({ title: "Dashboard" }, Tina4::HTTP_OK)
|
|
640
|
+
end
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
## ORM Pattern
|
|
644
|
+
|
|
645
|
+
```ruby
|
|
646
|
+
class User < Tina4::ORM
|
|
647
|
+
table_name "users"
|
|
648
|
+
integer_field :id, primary_key: true, auto_increment: true
|
|
649
|
+
string_field :name, required: true
|
|
650
|
+
string_field :email
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# Usage
|
|
654
|
+
user = User.create(name: "Alice", email: "alice@example.com")
|
|
655
|
+
users = User.where("name LIKE ?", ["%ali%"])
|
|
656
|
+
user = User.find(1)
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
## Template Pattern
|
|
660
|
+
|
|
661
|
+
```twig
|
|
662
|
+
{% extends "base.twig" %}
|
|
663
|
+
{% block content %}
|
|
664
|
+
<div class="container">
|
|
665
|
+
<h1>{{ title }}</h1>
|
|
666
|
+
{% for item in items %}
|
|
667
|
+
<p>{{ item.name }}</p>
|
|
668
|
+
{% endfor %}
|
|
669
|
+
</div>
|
|
670
|
+
{% endblock %}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Conventions
|
|
674
|
+
|
|
675
|
+
1. Routes return `response.call(data, status)` — never `puts` or `render`
|
|
676
|
+
2. GET routes are public; POST/PUT/PATCH/DELETE require auth by default
|
|
677
|
+
3. Every template extends `base.twig`
|
|
678
|
+
4. All schema changes via migrations — never create tables in route code
|
|
679
|
+
5. Use built-in features — never install gems for things Tina4 already provides
|
|
680
|
+
6. Service pattern — complex logic in `src/app/`, routes stay thin
|
|
681
|
+
7. Use `snake_case` for methods and variables
|
|
682
|
+
8. Wrap route logic in `begin/rescue`, log with `Tina4::Log.error()`
|
|
683
|
+
9. Database drivers: SQLite, PostgreSQL, MySQL, MSSQL, Firebird
|
|
684
|
+
|
|
685
|
+
## Built-in Features (No Gems Needed)
|
|
686
|
+
|
|
687
|
+
Router, ORM, Database (SQLite/PostgreSQL/MySQL/MSSQL/Firebird), Frond templates (Twig-compatible), JWT auth, Sessions (File/Redis/Valkey/MongoDB/DB), GraphQL + GraphiQL, WebSocket + Redis backplane, WSDL/SOAP, Queue (File/RabbitMQ/Kafka/MongoDB), HTTP client, Messenger (SMTP/IMAP), FakeData/Seeder, Migrations, SCSS compiler, Swagger/OpenAPI, i18n, Events, Container/DI, HtmlElement, Inline testing, Error overlay, Dev dashboard, Rate limiter, Response cache, Logging, MCP server
|
|
688
|
+
|
|
689
|
+
## Testing
|
|
690
|
+
|
|
691
|
+
Run: `bundle exec rspec` or `tina4ruby test`. Tests live in `spec/`. Use `Tina4::Testing` for inline tests.
|
|
692
|
+
CONTEXT
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
end
|