tina4ruby 3.10.32 → 3.10.38

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e75cf8a64755d61b6f6037110a61dfd2183bc46c7e77ce04aba13af145a64268
4
- data.tar.gz: f6d0b3e2f5bd0133acbcad91d322ae9ab3410c62eb2847f0edff5aeef94cc4f3
3
+ metadata.gz: 0a0442856facd7be61939efc9066b395c7578a2b7bd8df31282e02fdec58b95c
4
+ data.tar.gz: 33170414bbc5ed736aab77ce1790b62e90a74a62d51fa3f194b154b69acd34c4
5
5
  SHA512:
6
- metadata.gz: 16c4159dd47a39207e5d48a9b4830e884d4d7ea255c7c1dd3b33f6f03e7734d70445d4de57da16a64f8c3080c6ff5218de19b2009823b3f19f56eff8d72c661e
7
- data.tar.gz: ca9b35153be0b03cd92d56d51979a2532ff87eef0a4f5316bd28c050b0e591fa7bea81a7c5b5688749056b9873f5956a9136c0a5b6246e8ab3016ac54998e08b
6
+ metadata.gz: 237cd6644e93ee8ed01754a550a65cd5b43d9da8771631e7cff0745bb3e26e2e2892ddbea96e619bf264b527ce207276436c9f8a5c444f0ed1c28486a27ebe31
7
+ data.tar.gz: 71fb5d29f52b2aa9971424fbfddcc236881764a7bf6af0bcf491aa0a587d86d1ffcbd6ecfca5296bdbf3d2ee5d504d40eff2ea3eb4753c31dbb0db5c89326690
data/lib/tina4/ai.rb CHANGED
@@ -1,222 +1,113 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fileutils"
4
+
3
5
  module Tina4
4
- # Detect AI coding assistants and scaffold Tina4 context files.
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.
5
10
  #
6
- # Usage:
7
- # tools = Tina4::AI.detect_ai("/path/to/project")
8
- # files = Tina4::AI.install_ai_context("/path/to/project")
11
+ # selection = Tina4::AI.show_menu(".")
12
+ # Tina4::AI.install_selected(".", selection)
9
13
  #
10
14
  module AI
11
- AI_TOOLS = {
12
- "claude-code" => {
13
- description: "Claude Code (Anthropic CLI)",
14
- detect: ->(root) { Dir.exist?(File.join(root, ".claude")) || File.exist?(File.join(root, "CLAUDE.md")) },
15
- config_dir: ".claude",
16
- context_file: "CLAUDE.md"
17
- },
18
- "cursor" => {
19
- description: "Cursor IDE",
20
- detect: ->(root) { Dir.exist?(File.join(root, ".cursor")) || File.exist?(File.join(root, ".cursorules")) },
21
- config_dir: ".cursor",
22
- context_file: ".cursorules"
23
- },
24
- "copilot" => {
25
- description: "GitHub Copilot",
26
- detect: ->(root) { File.exist?(File.join(root, ".github", "copilot-instructions.md")) || Dir.exist?(File.join(root, ".github")) },
27
- config_dir: ".github",
28
- context_file: ".github/copilot-instructions.md"
29
- },
30
- "windsurf" => {
31
- description: "Windsurf (Codeium)",
32
- detect: ->(root) { File.exist?(File.join(root, ".windsurfrules")) },
33
- config_dir: nil,
34
- context_file: ".windsurfrules"
35
- },
36
- "aider" => {
37
- description: "Aider",
38
- detect: ->(root) { File.exist?(File.join(root, ".aider.conf.yml")) || File.exist?(File.join(root, "CONVENTIONS.md")) },
39
- config_dir: nil,
40
- context_file: "CONVENTIONS.md"
41
- },
42
- "cline" => {
43
- description: "Cline (VS Code)",
44
- detect: ->(root) { File.exist?(File.join(root, ".clinerules")) },
45
- config_dir: nil,
46
- context_file: ".clinerules"
47
- },
48
- "codex" => {
49
- description: "OpenAI Codex CLI",
50
- detect: ->(root) { File.exist?(File.join(root, "AGENTS.md")) || File.exist?(File.join(root, "codex.md")) },
51
- config_dir: nil,
52
- context_file: "AGENTS.md"
53
- }
54
- }.freeze
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
55
25
 
56
26
  class << self
57
- # Detect which AI coding tools are present in the project.
58
- #
59
- # @param root [String] project root directory (default: current directory)
60
- # @return [Array<Hash>] each hash has :name, :description, :config_file, :status
61
- def detect_ai(root = ".")
62
- root = File.expand_path(root)
63
- AI_TOOLS.map do |name, tool|
64
- installed = tool[:detect].call(root)
65
- {
66
- name: name,
67
- description: tool[:description],
68
- config_file: tool[:context_file],
69
- status: installed ? "detected" : "not detected"
70
- }
71
- end
72
- end
73
-
74
- # Return just the names of detected AI tools.
27
+ # Check if a tool's context file already exists.
75
28
  #
76
29
  # @param root [String] project root directory
77
- # @return [Array<String>]
78
- def detect_ai_names(root = ".")
79
- detect_ai(root).select { |t| t[:status] == "detected" }.map { |t| t[:name] }
30
+ # @param tool [Hash] tool entry from AI_TOOLS
31
+ # @return [Boolean]
32
+ def installed?(root, tool)
33
+ File.exist?(File.join(File.expand_path(root), tool[:context_file]))
80
34
  end
81
35
 
82
- # Install Tina4 context files for detected (or all) AI tools.
36
+ # Print the numbered menu and return user input.
83
37
  #
84
- # @param root [String] project root directory
85
- # @param tools [Array<String>, nil] specific tools to install for (nil = auto-detect)
86
- # @param force [Boolean] overwrite existing context files
87
- # @return [Array<String>] list of files created/updated
88
- def install_ai_context(root = ".", tools: nil, force: false)
38
+ # @param root [String] project root directory (default: ".")
39
+ # @return [String] user input (comma-separated numbers or "all")
40
+ def show_menu(root = ".")
89
41
  root = File.expand_path(root)
90
- created = []
91
-
92
- tool_names = tools || detect_ai_names(root)
93
- context = generate_context
94
-
95
- tool_names.each do |tool_name|
96
- tool = AI_TOOLS[tool_name]
97
- next unless tool
42
+ green = "\e[32m"
43
+ reset = "\e[0m"
98
44
 
99
- files = install_for_tool(root, tool_name, tool, context, force)
100
- created.concat(files)
45
+ puts "\n Tina4 AI Context Installer\n"
46
+ AI_TOOLS.each_with_index do |tool, i|
47
+ marker = installed?(root, tool) ? " #{green}[installed]#{reset}" : ""
48
+ puts format(" %d. %-20s %s%s", i + 1, tool[:description], tool[:context_file], marker)
101
49
  end
102
50
 
103
- created
104
- end
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
105
56
 
106
- # Install Tina4 context for ALL known AI tools (not just detected ones).
107
- #
108
- # @param root [String] project root directory
109
- # @param force [Boolean] overwrite existing context files
110
- # @return [Array<String>] list of files created/updated
111
- def install_all(root = ".", force: false)
112
- root = File.expand_path(root)
113
- created = []
114
- context = generate_context
115
-
116
- AI_TOOLS.each do |tool_name, tool|
117
- files = install_for_tool(root, tool_name, tool, context, force)
118
- created.concat(files)
119
- end
120
-
121
- created
57
+ print " Select (comma-separated, or 'all'): "
58
+ $stdin.gets&.strip || ""
122
59
  end
123
60
 
124
- # Generate a human-readable status report of AI tool detection.
61
+ # Install context files for the selected tools.
125
62
  #
126
63
  # @param root [String] project root directory
127
- # @return [String]
128
- def status_report(root = ".")
129
- tools = detect_ai(root)
130
- installed = tools.select { |t| t[:status] == "detected" }
131
- missing = tools.reject { |t| t[:status] == "detected" }
132
-
133
- lines = ["\nTina4 AI Context Status\n"]
134
-
135
- if installed.any?
136
- lines << "Detected AI tools:"
137
- installed.each { |t| lines << " + #{t[:description]} (#{t[:name]})" }
138
- else
139
- lines << "No AI coding tools detected."
140
- end
141
-
142
- if missing.any?
143
- lines << "\nNot detected (install context with `tina4ruby ai --all`):"
144
- missing.each { |t| lines << " - #{t[:description]} (#{t[:name]})" }
145
- end
146
-
147
- lines << ""
148
- lines.join("\n")
149
- end
150
-
151
- private
152
-
153
- def install_for_tool(root, name, tool, context, force)
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)
154
68
  created = []
155
- context_path = File.join(root, tool[:context_file])
156
69
 
157
- # Create config directory if needed
158
- if tool[:config_dir]
159
- FileUtils.mkdir_p(File.join(root, tool[:config_dir]))
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
160
85
  end
161
86
 
162
- # Ensure parent directory exists for the context file
163
- FileUtils.mkdir_p(File.dirname(context_path))
87
+ context = generate_context
164
88
 
165
- if !File.exist?(context_path) || force
166
- File.write(context_path, context)
167
- rel_path = context_path.sub("#{root}/", "")
168
- created << rel_path
89
+ indices.each do |idx|
90
+ tool = AI_TOOLS[idx]
91
+ files = install_for_tool(root_path, tool, context)
92
+ created.concat(files)
169
93
  end
170
94
 
171
- # Install Claude Code skills if it's Claude
172
- if name == "claude-code"
173
- skills = install_claude_skills(root, force)
174
- created.concat(skills)
175
- end
95
+ install_tina4_ai if do_tina4_ai
176
96
 
177
97
  created
178
98
  end
179
99
 
180
- def install_claude_skills(root, force)
181
- created = []
182
-
183
- # Determine the framework root (where lib/tina4/ lives)
184
- framework_root = File.expand_path("../../..", __FILE__)
185
-
186
- # Copy .skill files from the framework's skills/ directory to project root
187
- skills_source = File.join(framework_root, "skills")
188
- if Dir.exist?(skills_source)
189
- Dir.glob(File.join(skills_source, "*.skill")).each do |skill_file|
190
- target = File.join(root, File.basename(skill_file))
191
- if !File.exist?(target) || force
192
- FileUtils.cp(skill_file, target)
193
- created << File.basename(skill_file)
194
- end
195
- end
196
- end
197
-
198
- # Copy skill directories from .claude/skills/ in the framework to the project
199
- framework_skills_dir = File.join(framework_root, ".claude", "skills")
200
- if Dir.exist?(framework_skills_dir)
201
- target_skills_dir = File.join(root, ".claude", "skills")
202
- FileUtils.mkdir_p(target_skills_dir)
203
- Dir.children(framework_skills_dir).each do |entry|
204
- skill_dir = File.join(framework_skills_dir, entry)
205
- next unless File.directory?(skill_dir)
206
-
207
- target_dir = File.join(target_skills_dir, entry)
208
- if !Dir.exist?(target_dir) || force
209
- FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
210
- FileUtils.cp_r(skill_dir, target_dir)
211
- rel_path = target_dir.sub("#{root}/", "")
212
- created << rel_path
213
- end
214
- end
215
- end
216
-
217
- created
100
+ # Install context for all AI tools (non-interactive).
101
+ #
102
+ # @param root [String] project root directory
103
+ # @return [Array<String>] list of created/updated file paths
104
+ def install_all(root = ".")
105
+ install_selected(root, "all")
218
106
  end
219
107
 
108
+ # Generate the universal Tina4 Ruby context document for any AI assistant.
109
+ #
110
+ # @return [String]
220
111
  def generate_context
221
112
  <<~CONTEXT
222
113
  # Tina4 Ruby -- AI Context
@@ -230,7 +121,7 @@ module Tina4
230
121
 
231
122
  ```bash
232
123
  tina4ruby init . # Scaffold project
233
- tina4ruby start # Start dev server on port 7147
124
+ tina4ruby serve # Start dev server on port 7147
234
125
  tina4ruby migrate # Run database migrations
235
126
  tina4ruby test # Run test suite
236
127
  tina4ruby routes # List all registered routes
@@ -239,38 +130,43 @@ module Tina4
239
130
  ## Project Structure
240
131
 
241
132
  ```
242
- routes/ -- Route handlers (auto-discovered, one per resource)
243
- src/routes/ -- Alternative route location
244
- templates/ -- Twig/ERB templates (extends base template)
245
- public/ -- Static assets served at /
246
- scss/ -- SCSS files (auto-compiled to public/css/)
133
+ lib/tina4/ -- Core framework modules
134
+ src/routes/ -- Route handlers (auto-discovered, one per resource)
135
+ src/orm/ -- ORM models (one per file, filename = class name)
136
+ src/templates/ -- Twig/ERB templates (extends base template)
137
+ src/app/ -- Shared helpers and service classes
138
+ src/scss/ -- SCSS files (auto-compiled to public/css/)
139
+ src/public/ -- Static assets served at /
140
+ src/locales/ -- Translation JSON files
141
+ src/seeds/ -- Database seeder scripts
247
142
  migrations/ -- SQL migration files (sequential numbered)
248
- seeds/ -- Database seeder scripts
249
143
  spec/ -- RSpec test files
250
144
  ```
251
145
 
252
146
  ## Built-in Features (No External Gems Needed for Core)
253
147
 
254
- | Feature | Module |
255
- |---------|--------|
256
- | Routing | Tina4::Router |
257
- | ORM | Tina4::ORM |
258
- | Database | Tina4::Database |
259
- | Templates | Tina4::Template |
260
- | JWT Auth | Tina4::Auth |
261
- | REST API Client | Tina4::API |
262
- | GraphQL | Tina4::GraphQL |
263
- | WebSocket | Tina4::WebSocket |
264
- | SOAP/WSDL | Tina4::WSDL |
265
- | Email (SMTP+IMAP) | Tina4::Messenger |
266
- | Background Queue | Tina4::Queue |
267
- | SCSS Compilation | Tina4::ScssCompiler |
268
- | Migrations | Tina4::Migration |
269
- | Seeder | Tina4::FakeData |
270
- | i18n | Tina4::Localization |
271
- | Swagger/OpenAPI | Tina4::Swagger |
272
- | Sessions | Tina4::Session |
273
- | Middleware | Tina4::Middleware |
148
+ | Feature | Module | Require |
149
+ |---------|--------|---------|
150
+ | Routing | Tina4::Router | `require "tina4/router"` |
151
+ | ORM | Tina4::ORM | `require "tina4/orm"` |
152
+ | Database | Tina4::Database | `require "tina4/database"` |
153
+ | Templates | Tina4::Template | `require "tina4/template"` |
154
+ | JWT Auth | Tina4::Auth | `require "tina4/auth"` |
155
+ | REST API Client | Tina4::API | `require "tina4/api"` |
156
+ | GraphQL | Tina4::GraphQL | `require "tina4/graphql"` |
157
+ | WebSocket | Tina4::WebSocket | `require "tina4/websocket"` |
158
+ | SOAP/WSDL | Tina4::WSDL | `require "tina4/wsdl"` |
159
+ | Email (SMTP+IMAP) | Tina4::Messenger | `require "tina4/messenger"` |
160
+ | Background Queue | Tina4::Queue | `require "tina4/queue"` |
161
+ | SCSS Compilation | Tina4::ScssCompiler | `require "tina4/scss_compiler"` |
162
+ | Migrations | Tina4::Migration | `require "tina4/migration"` |
163
+ | Seeder | Tina4::FakeData | `require "tina4/seeder"` |
164
+ | i18n | Tina4::Localization | `require "tina4/localization"` |
165
+ | Swagger/OpenAPI | Tina4::Swagger | `require "tina4/swagger"` |
166
+ | Sessions | Tina4::Session | `require "tina4/session"` |
167
+ | Middleware | Tina4::Middleware | `require "tina4/middleware"` |
168
+ | HTML Builder | Tina4::HtmlElement | `require "tina4/html_element"` |
169
+ | Form Tokens | Tina4::Template | `{{ form_token() }}` in Twig |
274
170
 
275
171
  ## Key Conventions
276
172
 
@@ -283,6 +179,30 @@ module Tina4
283
179
  7. **Service pattern** -- complex logic goes in service classes, routes stay thin
284
180
  8. **Use built-in features** -- never install gems for things Tina4 already provides
285
181
 
182
+ ## AI Workflow -- Available Skills
183
+
184
+ When using an AI coding assistant with Tina4, these skills are available:
185
+
186
+ | Skill | Description |
187
+ |-------|-------------|
188
+ | `/tina4-route` | Create a new route with proper decorators and auth |
189
+ | `/tina4-orm` | Create an ORM model with migration |
190
+ | `/tina4-crud` | Generate complete CRUD (migration, ORM, routes, template, tests) |
191
+ | `/tina4-auth` | Set up JWT authentication with login/register |
192
+ | `/tina4-api` | Create an external API integration |
193
+ | `/tina4-queue` | Set up background job processing |
194
+ | `/tina4-template` | Create a server-rendered template page |
195
+ | `/tina4-graphql` | Set up a GraphQL endpoint |
196
+ | `/tina4-websocket` | Set up WebSocket communication |
197
+ | `/tina4-wsdl` | Create a SOAP/WSDL service |
198
+ | `/tina4-messenger` | Set up email send/receive |
199
+ | `/tina4-test` | Write tests for a feature |
200
+ | `/tina4-migration` | Create a database migration |
201
+ | `/tina4-seed` | Generate fake data for development |
202
+ | `/tina4-i18n` | Set up internationalization |
203
+ | `/tina4-scss` | Set up SCSS stylesheets |
204
+ | `/tina4-frontend` | Set up a frontend framework |
205
+
286
206
  ## Common Patterns
287
207
 
288
208
  ### Route
@@ -305,8 +225,114 @@ module Tina4
305
225
  numeric_field :price
306
226
  end
307
227
  ```
228
+
229
+ ### Template
230
+ ```twig
231
+ {% extends "base.twig" %}
232
+ {% block content %}
233
+ <div class="container">
234
+ <h1>{{ title }}</h1>
235
+ {% for item in items %}
236
+ <p>{{ item.name }}</p>
237
+ {% endfor %}
238
+ </div>
239
+ {% endblock %}
240
+ ```
308
241
  CONTEXT
309
242
  end
243
+
244
+ # Install context file for a single tool.
245
+ #
246
+ # @param root [String] absolute project root path
247
+ # @param tool [Hash] tool entry from AI_TOOLS
248
+ # @param context [String] generated context content
249
+ # @return [Array<String>] list of created/updated relative file paths
250
+ def install_for_tool(root, tool, context)
251
+ created = []
252
+ context_path = File.join(root, tool[:context_file])
253
+
254
+ # Create directories
255
+ if tool[:config_dir]
256
+ FileUtils.mkdir_p(File.join(root, tool[:config_dir]))
257
+ end
258
+ FileUtils.mkdir_p(File.dirname(context_path))
259
+
260
+ # Always overwrite -- user chose to install
261
+ action = File.exist?(context_path) ? "Updated" : "Installed"
262
+ File.write(context_path, context)
263
+ rel = context_path.sub("#{root}/", "")
264
+ created << rel
265
+ puts " \e[32m✓\e[0m #{action} #{rel}"
266
+
267
+ # Claude-specific extras
268
+ if tool[:name] == "claude-code"
269
+ skills = install_claude_skills(root)
270
+ created.concat(skills)
271
+ end
272
+
273
+ created
274
+ end
275
+
276
+ # Copy Claude Code skill files from the framework's templates.
277
+ #
278
+ # @param root [String] absolute project root path
279
+ # @return [Array<String>] list of created/updated relative file paths
280
+ def install_claude_skills(root)
281
+ created = []
282
+
283
+ # Determine the framework root (where lib/tina4/ lives)
284
+ framework_root = File.expand_path("../../..", __FILE__)
285
+
286
+ # Copy skill directories from .claude/skills/ in the framework to the project
287
+ framework_skills_dir = File.join(framework_root, ".claude", "skills")
288
+ if Dir.exist?(framework_skills_dir)
289
+ target_skills_dir = File.join(root, ".claude", "skills")
290
+ FileUtils.mkdir_p(target_skills_dir)
291
+ Dir.children(framework_skills_dir).each do |entry|
292
+ skill_dir = File.join(framework_skills_dir, entry)
293
+ next unless File.directory?(skill_dir)
294
+
295
+ target_dir = File.join(target_skills_dir, entry)
296
+ FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
297
+ FileUtils.cp_r(skill_dir, target_dir)
298
+ rel = target_dir.sub("#{root}/", "")
299
+ created << rel
300
+ puts " \e[32m✓\e[0m Updated #{rel}"
301
+ end
302
+ end
303
+
304
+ # Copy claude-commands if they exist
305
+ commands_source = File.join(framework_root, "templates", "ai", "claude-commands")
306
+ if Dir.exist?(commands_source)
307
+ commands_dir = File.join(root, ".claude", "commands")
308
+ FileUtils.mkdir_p(commands_dir)
309
+ Dir.glob(File.join(commands_source, "*.md")).each do |skill_file|
310
+ target = File.join(commands_dir, File.basename(skill_file))
311
+ FileUtils.cp(skill_file, target)
312
+ rel = target.sub("#{root}/", "")
313
+ created << rel
314
+ end
315
+ end
316
+
317
+ created
318
+ end
319
+
320
+ # Install tina4-ai package (provides mdview for markdown viewing).
321
+ def install_tina4_ai
322
+ puts " Installing tina4-ai tools..."
323
+ %w[pip3 pip].each do |cmd|
324
+ next unless system("which #{cmd} > /dev/null 2>&1")
325
+
326
+ result = `#{cmd} install --upgrade tina4-ai 2>&1`
327
+ if $?.success?
328
+ puts " \e[32m✓\e[0m Installed tina4-ai (mdview)"
329
+ return
330
+ else
331
+ puts " \e[33m!\e[0m #{cmd} failed: #{result.strip[0..100]}"
332
+ end
333
+ end
334
+ puts " \e[33m!\e[0m Python/pip not available -- skip tina4-ai"
335
+ end
310
336
  end
311
337
  end
312
338
  end
data/lib/tina4/cli.rb CHANGED
@@ -361,30 +361,22 @@ module Tina4
361
361
  # ── ai ────────────────────────────────────────────────────────────────
362
362
 
363
363
  def cmd_ai(argv)
364
- options = { all: false, force: false }
364
+ options = { all: false }
365
365
  parser = OptionParser.new do |opts|
366
366
  opts.banner = "Usage: tina4ruby ai [options]"
367
- opts.on("--all", "Install context for ALL AI tools (not just detected ones)") { options[:all] = true }
368
- opts.on("--force", "Overwrite existing context files") { options[:force] = true }
367
+ opts.on("--all", "Install context for ALL AI tools (non-interactive)") { options[:all] = true }
369
368
  end
370
369
  parser.parse!(argv)
371
370
 
372
371
  require_relative "ai"
373
372
 
374
373
  root_dir = Dir.pwd
375
- puts Tina4::AI.status_report(root_dir)
376
374
 
377
375
  if options[:all]
378
- created = Tina4::AI.install_all(root_dir, force: options[:force])
376
+ Tina4::AI.install_all(root_dir)
379
377
  else
380
- created = Tina4::AI.install_ai_context(root_dir, force: options[:force])
381
- end
382
-
383
- if created.any?
384
- puts "Created/updated context files:"
385
- created.each { |f| puts " #{f}" }
386
- else
387
- puts "No context files were created (files already exist; use --force to overwrite)."
378
+ selection = Tina4::AI.show_menu(root_dir)
379
+ Tina4::AI.install_selected(root_dir, selection) unless selection.empty?
388
380
  end
389
381
  end
390
382