strata-cli 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +26 -2
  4. data/lib/strata/cli/agent_mode.rb +26 -0
  5. data/lib/strata/cli/agent_output.rb +21 -0
  6. data/lib/strata/cli/ai/services/table_generator.rb +35 -20
  7. data/lib/strata/cli/api/client.rb +23 -63
  8. data/lib/strata/cli/api/response_error_handler.rb +115 -0
  9. data/lib/strata/cli/error_reporter.rb +4 -1
  10. data/lib/strata/cli/generators/datasource.rb +4 -3
  11. data/lib/strata/cli/generators/group.rb +37 -0
  12. data/lib/strata/cli/generators/migration.rb +2 -1
  13. data/lib/strata/cli/generators/project.rb +18 -11
  14. data/lib/strata/cli/generators/relation.rb +2 -1
  15. data/lib/strata/cli/generators/table.rb +5 -8
  16. data/lib/strata/cli/generators/templates/AGENTS.md +457 -88
  17. data/lib/strata/cli/generators/templates/table.table_name.yml +8 -3
  18. data/lib/strata/cli/generators/test.rb +2 -1
  19. data/lib/strata/cli/guard.rb +4 -1
  20. data/lib/strata/cli/helpers/command_context.rb +8 -9
  21. data/lib/strata/cli/helpers/datasource_helper.rb +27 -1
  22. data/lib/strata/cli/helpers/description_helper.rb +2 -1
  23. data/lib/strata/cli/main.rb +12 -3
  24. data/lib/strata/cli/output.rb +103 -0
  25. data/lib/strata/cli/sub_commands/audit.rb +136 -16
  26. data/lib/strata/cli/sub_commands/branch.rb +165 -0
  27. data/lib/strata/cli/sub_commands/create.rb +13 -2
  28. data/lib/strata/cli/sub_commands/datasource.rb +21 -3
  29. data/lib/strata/cli/sub_commands/deploy.rb +16 -13
  30. data/lib/strata/cli/sub_commands/project.rb +6 -3
  31. data/lib/strata/cli/sub_commands/table.rb +11 -8
  32. data/lib/strata/cli/terminal.rb +7 -4
  33. data/lib/strata/cli/ui/field_editor.rb +21 -27
  34. data/lib/strata/cli/utils/deployment_monitor.rb +15 -34
  35. data/lib/strata/cli/utils/git.rb +78 -0
  36. data/lib/strata/cli/utils/import_manager.rb +4 -1
  37. data/lib/strata/cli/utils/test_reporter.rb +4 -32
  38. data/lib/strata/cli/utils/version_checker.rb +4 -8
  39. data/lib/strata/cli/utils.rb +3 -1
  40. data/lib/strata/cli/version.rb +1 -1
  41. data/lib/strata/cli.rb +4 -3
  42. metadata +6 -2
  43. data/lib/strata/cli/helpers/color_helper.rb +0 -103
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../guard"
4
+ require_relative "../output"
5
+ require_relative "../api/client"
6
+ require_relative "../utils/git"
7
+ require "time"
8
+ require "tty-prompt"
9
+ require_relative "../agent_mode"
10
+
11
+ module Strata
12
+ module CLI
13
+ module SubCommands
14
+ class Branch < Thor
15
+ include Guard
16
+ include Output
17
+ include AgentMode
18
+
19
+ default_command :list
20
+ class_option :environment, aliases: ["e"], type: :string
21
+
22
+ desc "list", "List local and Strata server branches"
23
+ def list
24
+ config = CLI.config.get_for_environment(options[:environment])
25
+ local_branch_names = Utils::Git.local_branches
26
+ server_branches = fetch_server_branches(config)
27
+
28
+ rows = branch_rows(local_branch_names, server_branches)
29
+ if rows.empty?
30
+ print_info("No local or Strata server branches found.")
31
+ else
32
+ rows.each { |row| print_info(row) }
33
+ end
34
+ end
35
+
36
+ desc "create BRANCH", "Create and checkout a local git branch"
37
+ def create(branch_name)
38
+ branch_name = Utils::Git.create_and_checkout_branch(branch_name)
39
+ print_success("Created and checked out branch '#{branch_name}'.")
40
+ end
41
+
42
+ desc "checkout BRANCH", "Checkout a local git branch"
43
+ def checkout(branch_name)
44
+ branch_name = Utils::Git.checkout_branch(branch_name)
45
+ print_success("Checked out branch '#{branch_name}'.")
46
+ end
47
+
48
+ desc "delete", "Delete a branch from Strata server"
49
+ method_option :branch, aliases: ["b"], type: :string, desc: "Branch to delete. Defaults to current git branch."
50
+ def delete
51
+ branch_id = branch_to_delete
52
+ config = CLI.config.get_for_environment(options[:environment])
53
+ validate_delete_configuration(config)
54
+
55
+ return unless confirm_branch_deletion(branch_id)
56
+
57
+ client = API::Client.new(config["server"], config["api_key"])
58
+ server_deleted = client.delete_branch(config["project_id"], branch_id)
59
+ print_warning("Branch '#{branch_id}' was not found on Strata server. Deleting local branch only.") unless server_deleted
60
+
61
+ delete_local_branch(branch_id)
62
+
63
+ print_success("Deleted branch '#{branch_id}'.")
64
+ end
65
+
66
+ private
67
+
68
+ def branch_to_delete
69
+ explicit_branch = options[:branch].to_s.strip
70
+ branch_id = explicit_branch.empty? ? Utils::Git.current_branch.to_s.strip : explicit_branch
71
+
72
+ if branch_id.empty?
73
+ raise Strata::CommandError, "Branch name is required. Use -b BRANCH to specify one."
74
+ end
75
+
76
+ if branch_id == "HEAD"
77
+ raise Strata::CommandError, "Cannot determine current branch while in detached HEAD. Use -b BRANCH to specify one."
78
+ end
79
+
80
+ branch_id
81
+ end
82
+
83
+ def validate_delete_configuration(config)
84
+ ensure_config_present(config, "server")
85
+ ensure_config_present(config, "api_key")
86
+ ensure_config_present(config, "project_id")
87
+ end
88
+
89
+ def ensure_config_present(config, key)
90
+ return if config[key] && !config[key].to_s.strip.empty?
91
+
92
+ raise Strata::CommandError, "Missing required configuration: #{key}. Check your project.yml or .strata file."
93
+ end
94
+
95
+ def confirm_branch_deletion(branch_id)
96
+ prompt = TTY::Prompt.new
97
+ answer = prompt.ask("Type '#{branch_id}' to delete this branch from Strata:")
98
+ return true if answer.to_s == branch_id
99
+
100
+ print_warning("Branch deletion cancelled.")
101
+ false
102
+ end
103
+
104
+ def fetch_server_branches(config)
105
+ unless server_configured?(config)
106
+ print_warning("Server configuration is incomplete. Showing local branches only.")
107
+ return []
108
+ end
109
+
110
+ API::Client.new(config["server"], config["api_key"]).branches(config["project_id"])
111
+ end
112
+
113
+ def server_configured?(config)
114
+ %w[server api_key project_id].all? { |key| config[key] && !config[key].to_s.strip.empty? }
115
+ end
116
+
117
+ def branch_rows(local_branch_names, server_branches)
118
+ local_names = local_branch_names.to_h { |name| [name, true] }
119
+ server_by_uid = server_branches.to_h { |branch| [branch["uid"], branch] }
120
+ current_branch = Utils::Git.git_repo? ? Utils::Git.current_branch : nil
121
+
122
+ (local_names.keys | server_by_uid.keys).sort.map do |branch_name|
123
+ server_branch = server_by_uid[branch_name]
124
+ "#{current_branch_marker(branch_name, current_branch)}(#{branch_location(local_names.key?(branch_name), server_branch)}) - last deployed at: #{format_last_deployed_at(server_branch && server_branch["last_deployed_at"])}"
125
+ end
126
+ end
127
+
128
+ def current_branch_marker(branch_name, current_branch)
129
+ (current_branch == branch_name) ? "* #{branch_name}" : branch_name
130
+ end
131
+
132
+ def branch_location(local, server_branch)
133
+ tags = []
134
+ tags << "local" if local
135
+ tags << "server" if server_branch
136
+ tags.empty? ? "unknown" : tags.join(", ")
137
+ end
138
+
139
+ def format_last_deployed_at(value)
140
+ return "" if value.nil? || value.to_s.strip.empty?
141
+
142
+ Time.parse(value.to_s).strftime("%Y-%m-%d %H:%M:%S")
143
+ rescue ArgumentError
144
+ value.to_s
145
+ end
146
+
147
+ def delete_local_branch(branch_id)
148
+ case Utils::Git.delete_local_branch(branch_id, fallback_branches: local_delete_fallback_branches)
149
+ when :deleted
150
+ print_info("Deleted local git branch '#{branch_id}'.")
151
+ when :not_found
152
+ print_warning("Local git branch '#{branch_id}' was not found.")
153
+ when :not_git_repo
154
+ print_warning("No local git repository found. Skipped local branch deletion.")
155
+ end
156
+ end
157
+
158
+ def local_delete_fallback_branches
159
+ production_branch = CLI.config["production_branch"].to_s.strip
160
+ ([production_branch] + %w[main master]).reject(&:empty?).uniq
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -7,10 +7,10 @@ require_relative "../ui/field_editor"
7
7
  require_relative "../credentials"
8
8
  require_relative "../helpers/table_filter"
9
9
  require_relative "../ai/services/table_generator"
10
- require_relative "../helpers/color_helper"
11
10
  require_relative "../helpers/prompts"
12
11
  require_relative "../helpers/description_helper"
13
12
  require_relative "../helpers/command_context"
13
+ require_relative "../agent_mode"
14
14
  require "tty-prompt"
15
15
  require "tty-spinner"
16
16
  require "yaml"
@@ -26,6 +26,7 @@ module Strata
26
26
  include Prompts
27
27
  include Thor::Actions
28
28
  include Helpers::CommandContext
29
+ include AgentMode
29
30
  extend Helpers::DescriptionHelper
30
31
 
31
32
  desc "table TABLE_PATH", "Create a semantic table model"
@@ -35,6 +36,11 @@ module Strata
35
36
  desc: "Datasource key from datasources.yml"
36
37
 
37
38
  def table(table_path = nil)
39
+ reject_agent_mode!(
40
+ "Agent mode: write models/tbl.*.yml directly. Use: strata datasource meta DS_KEY TABLE_NAME --agent",
41
+ code: "use_yaml_files"
42
+ )
43
+
38
44
  return unless datasource_key
39
45
 
40
46
  # If table_path provided, skip search for speed
@@ -52,6 +58,11 @@ module Strata
52
58
  desc: "Datasource key from datasources.yml"
53
59
 
54
60
  def relation(relation_path)
61
+ reject_agent_mode!(
62
+ "Agent mode: write models/rel.*.yml directly.",
63
+ code: "use_yaml_files"
64
+ )
65
+
55
66
  return unless datasource_key
56
67
 
57
68
  create_relation_file(relation_path)
@@ -406,7 +417,7 @@ module Strata
406
417
 
407
418
  return unless from.strip == to.strip
408
419
 
409
- say "Warning: --from and --to have the same value. Migration will have no effect.", ColorHelper.warning
420
+ print_warning("Warning: --from and --to have the same value. Migration will have no effect.")
410
421
  end
411
422
 
412
423
  def prompt_migration_hook(operation)
@@ -3,10 +3,12 @@
3
3
  require_relative "../guard"
4
4
  require_relative "../credentials"
5
5
  require_relative "../terminal"
6
+ require_relative "../output"
6
7
  require_relative "../utils/git"
7
8
  require "tty-prompt"
8
9
  require_relative "../helpers/datasource_helper"
9
10
  require_relative "../helpers/description_helper"
11
+ require_relative "../agent_mode"
10
12
 
11
13
  module Strata
12
14
  module CLI
@@ -15,7 +17,9 @@ module Strata
15
17
  include Thor::Actions
16
18
  include Guard
17
19
  include Terminal
20
+ include Output
18
21
  include DatasourceHelper
22
+ include AgentMode
19
23
  extend Helpers::DescriptionHelper
20
24
 
21
25
  desc "adapters", "Lists supported data warehouse adapters"
@@ -50,7 +54,7 @@ module Strata
50
54
  return
51
55
  end
52
56
 
53
- names = ds.keys.map { "#{it} => #{ds[it]["name"]}" }
57
+ names = ds.keys.map { "Key: #{it} | Name: #{ds[it]["name"]}" }
54
58
  out = "\n #{names.join("\n ")}"
55
59
  say out, :magenta
56
60
  end
@@ -182,11 +186,17 @@ module Strata
182
186
  ds_key = resolve_datasource(ds_key, prompt: prompt)
183
187
  return unless ds_key
184
188
 
185
- say "\nListing #{ds_key} tables...\n\n", :yellow
186
189
  adapter = create_adapter(ds_key)
187
190
  tables = adapter.tables(**options)
188
191
  tables = tables.select { it =~ /#{options[:pattern]}/ } if options[:pattern]
189
192
 
193
+ if agent_mode?
194
+ AgentOutput.emit_json({datasource: ds_key, tables: tables})
195
+ return
196
+ end
197
+
198
+ say "\nListing #{ds_key} tables...\n\n", :yellow
199
+
190
200
  if tables.empty?
191
201
  say "No tables found.", :yellow
192
202
  return
@@ -203,10 +213,18 @@ module Strata
203
213
  method_option :catalog, aliases: "c", type: :string, desc: "Change the catalog from the configured one."
204
214
  method_option :schema, aliases: "s", type: :string, desc: "Change the schema from the configured one."
205
215
  def meta(ds_key, table_name)
206
- say "\n● Schema for table: #{table_name} (#{ds_key}):\n", :yellow
207
216
  adapter = create_adapter(ds_key)
208
217
  md = adapter.metadata(table_name, **options.transform_keys(&:to_sym))
209
218
 
219
+ if agent_mode?
220
+ columns = md.columns.map do |column|
221
+ column.to_h.merge(normalized_data_type: column.normalized_data_type)
222
+ end
223
+ AgentOutput.emit_json({datasource: ds_key, table: table_name, columns: columns})
224
+ return
225
+ end
226
+
227
+ say "\n● Schema for table: #{table_name} (#{ds_key}):\n", :yellow
210
228
  headings = md.columns.first.to_h.keys
211
229
  rows = md.columns.map(&:to_h).map(&:values)
212
230
 
@@ -2,9 +2,10 @@
2
2
 
3
3
  require_relative "../guard"
4
4
  require_relative "../terminal"
5
- require_relative "../helpers/color_helper"
5
+ require_relative "../output"
6
6
  require_relative "../helpers/project_helper"
7
7
  require_relative "../helpers/description_helper"
8
+ require_relative "../agent_mode"
8
9
  require_relative "../api/client"
9
10
  require_relative "../credentials"
10
11
  require_relative "../utils"
@@ -22,6 +23,8 @@ module Strata
22
23
  class Deploy < Thor
23
24
  include Guard
24
25
  include Terminal
26
+ include Output
27
+ include AgentMode
25
28
  extend Helpers::DescriptionHelper
26
29
 
27
30
  default_command :deploy
@@ -57,7 +60,7 @@ module Strata
57
60
  # If only external imports changed, generate synthetic commit identifier
58
61
  if refreshed_imports.any? && local_file_changes.empty?
59
62
  import_commit_hash = Utils::ImportManager.generate_import_commit_hash(project_path)
60
- say "\nExternal imports change found. Proceeding with deployment", ColorHelper.info
63
+ print_info("\nExternal imports change found. Proceeding with deployment")
61
64
  metadata[:commit] = "imports-#{import_commit_hash}"
62
65
  metadata[:commit_message] = "External imports updated: #{refreshed_imports.map do |c|
63
66
  File.basename(c[:source])
@@ -68,7 +71,7 @@ module Strata
68
71
 
69
72
  env_display = options[:environment] ? " (#{options[:environment]} environment)" : ""
70
73
  server_info = config["server"] ? " (#{config["server"]})" : ""
71
- say "\nDeploying to branch '#{branch_id}' on Strata server#{env_display}#{server_info}\n", ColorHelper.info
74
+ print_info("\nDeploying to branch '#{branch_id}' on Strata server#{env_display}#{server_info}\n")
72
75
 
73
76
  deployment = submit_deployment(config, branch_id, archive_path, metadata)
74
77
 
@@ -87,7 +90,7 @@ module Strata
87
90
  deployment = client.latest_deployment(config["project_id"], branch_id)
88
91
 
89
92
  unless deployment
90
- say "No deployments found for branch '#{branch_id}'.\n", ColorHelper.info
93
+ print_info("No deployments found for branch '#{branch_id}'.\n")
91
94
  return
92
95
  end
93
96
 
@@ -331,8 +334,8 @@ module Strata
331
334
 
332
335
  if changed_paths.empty? && refreshed_imports.empty?
333
336
  unless options[:force]
334
- say "\nNo files changed since last deployment.", ColorHelper.warning
335
- say "To force deploy, run command with --force or -f flag.\n", ColorHelper.info
337
+ print_warning("\nNo files changed since last deployment.")
338
+ print_info("To force deploy, run command with --force or -f flag.\n")
336
339
  exit(0)
337
340
  end
338
341
  Utils::Archive.create(project_path, file_overrides: file_overrides)
@@ -345,7 +348,7 @@ module Strata
345
348
  change_count = files_to_include.length
346
349
  change_count += refreshed_imports.length if refreshed_imports.any?
347
350
 
348
- say "Including #{change_count} changed file(s) in archive...\n", ColorHelper.info
351
+ print_info("Including #{change_count} changed file(s) in archive...\n")
349
352
  Utils::Archive.create(project_path, files_to_include: files_to_include, file_overrides: file_overrides)
350
353
  end
351
354
  else
@@ -412,7 +415,7 @@ module Strata
412
415
  message = "Deploying to production branch '#{branch_id}'. Continue? [y/N]"
413
416
  return if prompt.yes?(message, default: false)
414
417
 
415
- say "Deployment cancelled.", ColorHelper.warning
418
+ print_warning("Deployment cancelled.")
416
419
  exit(0)
417
420
  end
418
421
 
@@ -422,7 +425,7 @@ module Strata
422
425
  project_config = YAML.safe_load_file("project.yml", permitted_classes: [Date, Time], aliases: true) || {}
423
426
  project_config["production_branch"] || "main"
424
427
  rescue => e
425
- warn "Failed to load project.yml: #{e.message}" if ENV["DEBUG"]
428
+ print_warning("Failed to load project.yml: #{e.message}") if ENV["DEBUG"]
426
429
  "main"
427
430
  end
428
431
 
@@ -436,11 +439,11 @@ module Strata
436
439
  return nil unless last_deployment && last_deployment["commit"]
437
440
 
438
441
  commit_hash = last_deployment["commit"]
439
- say "Found last successful deployment at commit: #{commit_hash[0..7]}...\n", ColorHelper.info
442
+ print_info("Found last successful deployment at commit: #{commit_hash[0..7]}...\n")
440
443
  commit_hash
441
444
  rescue Strata::CommandError
442
445
  # If we can't get last deployment (e.g., first deployment), continue with all files
443
- say "No previous deployment found. Including all files.\n", ColorHelper.info
446
+ print_info("No previous deployment found. Including all files.\n")
444
447
  nil
445
448
  end
446
449
  end
@@ -477,10 +480,10 @@ module Strata
477
480
  refreshed = Utils::ImportManager.refresh_external_imports(project_path)
478
481
 
479
482
  if refreshed.any?
480
- say "\n Skimmed #{refreshed.length} external import(s):", ColorHelper.info
483
+ print_info("\n Skimmed #{refreshed.length} external import(s):")
481
484
  refreshed.each do |import|
482
485
  filename = File.basename(import[:source])
483
- say " • #{filename}", ColorHelper.info
486
+ print_info(" • #{filename}")
484
487
  end
485
488
  end
486
489
 
@@ -2,8 +2,9 @@
2
2
 
3
3
  require_relative "../guard"
4
4
  require_relative "../terminal"
5
- require_relative "../helpers/color_helper"
5
+ require_relative "../output"
6
6
  require_relative "../helpers/project_helper"
7
+ require_relative "../agent_mode"
7
8
  require_relative "../api/client"
8
9
  require "yaml"
9
10
 
@@ -13,6 +14,8 @@ module Strata
13
14
  class Project < Thor
14
15
  include Guard
15
16
  include Terminal
17
+ include Output
18
+ include AgentMode
16
19
 
17
20
  desc "link PROJECT_ID", "Link local project to an existing project on the server"
18
21
  def link(project_id)
@@ -22,13 +25,13 @@ module Strata
22
25
  project_config = YAML.safe_load_file(project_yml_path, permitted_classes: [Date, Time], aliases: true) || {}
23
26
 
24
27
  if project_config["project_id"] && !project_config["project_id"].to_s.strip.empty?
25
- say "Project is already linked to project_id: #{project_config["project_id"]}", ColorHelper.warning
28
+ print_warning("Project is already linked to project_id: #{project_config["project_id"]}")
26
29
  return
27
30
  end
28
31
 
29
32
  return unless Helpers::ProjectHelper.persist_project_id_to_yml(project_id, project_yml_path: project_yml_path)
30
33
 
31
- say "✓ Project linked successfully. project_id: #{project_id}", ColorHelper.info
34
+ print_info("✓ Project linked successfully. project_id: #{project_id}")
32
35
  end
33
36
  end
34
37
  end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../helpers/color_helper"
4
3
  require_relative "../helpers/prompts"
5
4
  require_relative "../helpers/command_context"
5
+ require_relative "../agent_mode"
6
+ require_relative "../output"
6
7
 
7
8
  module Strata
8
9
  module CLI
@@ -10,32 +11,34 @@ module Strata
10
11
  class Table < Thor
11
12
  include Guard
12
13
  include Terminal
14
+ include Output
13
15
  include Prompts
14
16
  include Thor::Actions
15
17
  include Helpers::CommandContext
18
+ include AgentMode
16
19
  extend Helpers::DescriptionHelper
17
20
 
18
21
  desc "list", "List all semantic models in the project"
19
22
  def list
20
23
  unless Dir.exist?("models")
21
- say MSG_NO_MODELS_DIR, :yellow
24
+ print_warning(MSG_NO_MODELS_DIR)
22
25
  return
23
26
  end
24
27
 
25
- model_files = Dir.glob("models/tbl[._]*.yml").sort
28
+ model_files = Dir.glob("models/**/tbl[._]*.yml").sort
26
29
 
27
30
  if model_files.empty?
28
- say MSG_NO_MODELS_FOUND, :yellow
31
+ print_warning(MSG_NO_MODELS_FOUND)
29
32
  return
30
33
  end
31
34
 
32
- say MSG_MODELS_LIST_HEADER, :cyan
35
+ print_info(MSG_MODELS_LIST_HEADER)
33
36
 
34
37
  model_files.each do |file|
35
38
  display_model_item(file)
36
39
  end
37
40
 
38
- say MSG_MODELS_COUNT % model_files.length, :cyan
41
+ print_info(MSG_MODELS_COUNT % model_files.length)
39
42
  end
40
43
 
41
44
  private
@@ -47,9 +50,9 @@ module Strata
47
50
  desc = "#{desc[0..50]}..." if desc.length > 50
48
51
 
49
52
  if desc.empty?
50
- say " #{name}", :white
53
+ print_info(" #{name}")
51
54
  else
52
- say " #{name.ljust(25)} #{desc}", :white
55
+ print_info(" #{name.ljust(25)} #{desc}")
53
56
  end
54
57
  end
55
58
  end
@@ -4,6 +4,7 @@ require "tty-spinner"
4
4
  require "pastel"
5
5
  require "tty-table"
6
6
  require "io/console"
7
+ require_relative "output"
7
8
 
8
9
  module Strata
9
10
  module CLI
@@ -23,13 +24,15 @@ module Strata
23
24
 
24
25
  # Shows a loader while running IO tasks
25
26
  # Uses consistent format matching deployment monitor: cyan spinner that transitions to checkmark
26
- def with_spinner(message = "Loading...",
27
+ def with_spinner(
28
+ message = "Loading...",
27
29
  success_message: "",
28
30
  failed_message: "",
29
31
  clear: false,
30
32
  message_color: :cyan,
31
33
  spinner_color: :cyan,
32
- format: :dots)
34
+ format: :dots
35
+ )
33
36
  spinner = create_spinner(message,
34
37
  message_color: message_color,
35
38
  spinner_color: spinner_color,
@@ -39,10 +42,10 @@ module Strata
39
42
 
40
43
  begin
41
44
  result = yield
42
- spinner.success(success_message.empty? ? "" : Pastel.new.green(success_message))
45
+ spinner.success(success_message.empty? ? "" : Output.format(:success, success_message))
43
46
  result
44
47
  rescue => e
45
- spinner.error(failed_message.empty? ? "" : Pastel.new.red(failed_message))
48
+ spinner.error(failed_message.empty? ? "" : Output.format(:error, failed_message))
46
49
  raise e
47
50
  end
48
51
  end
@@ -3,7 +3,7 @@
3
3
  require "tty-prompt"
4
4
  require "tty-table"
5
5
  require "pastel"
6
- require_relative "../helpers/color_helper"
6
+ require_relative "../output"
7
7
 
8
8
  module Strata
9
9
  module CLI
@@ -77,21 +77,19 @@ module Strata
77
77
  end
78
78
 
79
79
  def display_table
80
- colors = ColorHelper
81
- puts colors.highlight("\n Field Editor - Review & Edit Generated Fields\n")
80
+ puts Output.format(:highlight, "\n Field Editor - Review & Edit Generated Fields\n")
82
81
 
83
82
  rows = @fields.each_with_index.map do |field, idx|
84
- indicator = (idx == @selected_index) ? colors.selected("❯") : " "
85
- status = field[:skipped] ? colors.disabled("[SKIP]") : ""
83
+ indicator = (idx == @selected_index) ? Output.format(:selected, "❯") : " "
84
+ status = field[:skipped] ? Output.format(:disabled, "[SKIP]") : ""
86
85
 
87
- name_cell = field[:skipped] ? colors.disabled(field[:name]) : field[:name]
86
+ name_cell = field[:skipped] ? Output.format(:disabled, field[:name]) : field[:name]
88
87
  desc_cell = if field[:skipped]
89
- colors.disabled(truncate(field[:description],
90
- 25))
88
+ Output.format(:disabled, truncate(field[:description], 25))
91
89
  else
92
90
  truncate(field[:description], 25)
93
91
  end
94
- expr_cell = field[:skipped] ? colors.disabled(field[:expression]) : field[:expression]
92
+ expr_cell = field[:skipped] ? Output.format(:disabled, field[:expression]) : field[:expression]
95
93
 
96
94
  [
97
95
  "#{indicator} #{status}",
@@ -112,14 +110,13 @@ module Strata
112
110
  end
113
111
 
114
112
  def display_help
115
- colors = ColorHelper
116
113
  puts ""
117
- puts " #{colors.info("↑/↓")} Navigate " \
118
- "#{colors.success("[Enter]")} Edit " \
119
- "#{colors.warning("[S]")}kip " \
120
- "#{colors.primary("[P]")}rompt " \
121
- "#{colors.secondary("[C]")}onfirm " \
122
- "#{colors.error("[Q/Esc]")}uit"
114
+ puts " #{Output.format(:info, "↑/↓")} Navigate " \
115
+ "#{Output.format(:success, "[Enter]")} Edit " \
116
+ "#{Output.format(:warning, "[S]")}kip " \
117
+ "#{Output.format(:primary, "[P]")}rompt " \
118
+ "#{Output.format(:secondary, "[C]")}onfirm " \
119
+ "#{Output.format(:error, "[Q/Esc]")}uit"
123
120
  puts ""
124
121
  end
125
122
 
@@ -129,10 +126,9 @@ module Strata
129
126
 
130
127
  def edit_current_field
131
128
  field = @fields[@selected_index]
132
- colors = ColorHelper
133
129
 
134
- puts colors.highlight("\n Editing: #{field[:name]}\n")
135
- puts colors.dim(" (Press Ctrl+C or type 'back' to go back without saving)\n")
130
+ puts Output.format(:highlight, "\n Editing: #{field[:name]}\n")
131
+ puts Output.format(:dim, " (Press Ctrl+C or type 'back' to go back without saving)\n")
136
132
 
137
133
  begin
138
134
  field[:name] = prompt.ask(" Field Name:", default: field[:name])
@@ -170,20 +166,18 @@ module Strata
170
166
  end
171
167
 
172
168
  def handle_prompt_mode
173
- require_relative "../ai/services/model_generator"
169
+ require_relative "../ai/services/table_generator"
174
170
  require_relative "../terminal"
175
- generator = AI::Services::ModelGenerator.new
176
-
177
- colors = ColorHelper
171
+ generator = AI::Services::TableGenerator.new
178
172
 
179
173
  unless generator.ai_available?
180
- puts colors.warning("\n AI is not available. Configure AI in .strata file.")
174
+ puts Output.format(:warning, "\n AI is not available. Configure AI in .strata file.")
181
175
  sleep 1.5
182
176
  return
183
177
  end
184
178
 
185
179
  unless @table_context
186
- puts colors.warning("\n Prompt mode requires table context.")
180
+ puts Output.format(:warning, "\n Prompt mode requires table context.")
187
181
  sleep 1.5
188
182
  return
189
183
  end
@@ -208,11 +202,11 @@ module Strata
208
202
  @selected_index = 0
209
203
  # No message needed - table will refresh and show new fields
210
204
  else
211
- puts colors.warning(" No changes - keeping current fields")
205
+ puts Output.format(:warning, " No changes - keeping current fields")
212
206
  sleep 1
213
207
  end
214
208
  rescue => e
215
- puts colors.error("\n Error: #{e.message}")
209
+ puts Output.format(:error, "\n Error: #{e.message}")
216
210
  sleep 2
217
211
  end
218
212
  end