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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +26 -2
- data/lib/strata/cli/agent_mode.rb +26 -0
- data/lib/strata/cli/agent_output.rb +21 -0
- data/lib/strata/cli/ai/services/table_generator.rb +35 -20
- data/lib/strata/cli/api/client.rb +23 -63
- data/lib/strata/cli/api/response_error_handler.rb +115 -0
- data/lib/strata/cli/error_reporter.rb +4 -1
- data/lib/strata/cli/generators/datasource.rb +4 -3
- data/lib/strata/cli/generators/group.rb +37 -0
- data/lib/strata/cli/generators/migration.rb +2 -1
- data/lib/strata/cli/generators/project.rb +18 -11
- data/lib/strata/cli/generators/relation.rb +2 -1
- data/lib/strata/cli/generators/table.rb +5 -8
- data/lib/strata/cli/generators/templates/AGENTS.md +457 -88
- data/lib/strata/cli/generators/templates/table.table_name.yml +8 -3
- data/lib/strata/cli/generators/test.rb +2 -1
- data/lib/strata/cli/guard.rb +4 -1
- data/lib/strata/cli/helpers/command_context.rb +8 -9
- data/lib/strata/cli/helpers/datasource_helper.rb +27 -1
- data/lib/strata/cli/helpers/description_helper.rb +2 -1
- data/lib/strata/cli/main.rb +12 -3
- data/lib/strata/cli/output.rb +103 -0
- data/lib/strata/cli/sub_commands/audit.rb +136 -16
- data/lib/strata/cli/sub_commands/branch.rb +165 -0
- data/lib/strata/cli/sub_commands/create.rb +13 -2
- data/lib/strata/cli/sub_commands/datasource.rb +21 -3
- data/lib/strata/cli/sub_commands/deploy.rb +16 -13
- data/lib/strata/cli/sub_commands/project.rb +6 -3
- data/lib/strata/cli/sub_commands/table.rb +11 -8
- data/lib/strata/cli/terminal.rb +7 -4
- data/lib/strata/cli/ui/field_editor.rb +21 -27
- data/lib/strata/cli/utils/deployment_monitor.rb +15 -34
- data/lib/strata/cli/utils/git.rb +78 -0
- data/lib/strata/cli/utils/import_manager.rb +4 -1
- data/lib/strata/cli/utils/test_reporter.rb +4 -32
- data/lib/strata/cli/utils/version_checker.rb +4 -8
- data/lib/strata/cli/utils.rb +3 -1
- data/lib/strata/cli/version.rb +1 -1
- data/lib/strata/cli.rb +4 -3
- metadata +6 -2
- 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
|
-
|
|
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}
|
|
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 "../
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
483
|
+
print_info("\n Skimmed #{refreshed.length} external import(s):")
|
|
481
484
|
refreshed.each do |import|
|
|
482
485
|
filename = File.basename(import[:source])
|
|
483
|
-
|
|
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 "../
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
print_warning(MSG_NO_MODELS_DIR)
|
|
22
25
|
return
|
|
23
26
|
end
|
|
24
27
|
|
|
25
|
-
model_files = Dir.glob("models
|
|
28
|
+
model_files = Dir.glob("models/**/tbl[._]*.yml").sort
|
|
26
29
|
|
|
27
30
|
if model_files.empty?
|
|
28
|
-
|
|
31
|
+
print_warning(MSG_NO_MODELS_FOUND)
|
|
29
32
|
return
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
+
print_info(" #{name}")
|
|
51
54
|
else
|
|
52
|
-
|
|
55
|
+
print_info(" #{name.ljust(25)} #{desc}")
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
58
|
end
|
data/lib/strata/cli/terminal.rb
CHANGED
|
@@ -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(
|
|
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? ? "" :
|
|
45
|
+
spinner.success(success_message.empty? ? "" : Output.format(:success, success_message))
|
|
43
46
|
result
|
|
44
47
|
rescue => e
|
|
45
|
-
spinner.error(failed_message.empty? ? "" :
|
|
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 "../
|
|
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
|
-
|
|
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) ?
|
|
85
|
-
status = field[:skipped] ?
|
|
83
|
+
indicator = (idx == @selected_index) ? Output.format(:selected, "❯") : " "
|
|
84
|
+
status = field[:skipped] ? Output.format(:disabled, "[SKIP]") : ""
|
|
86
85
|
|
|
87
|
-
name_cell = field[:skipped] ?
|
|
86
|
+
name_cell = field[:skipped] ? Output.format(:disabled, field[:name]) : field[:name]
|
|
88
87
|
desc_cell = if field[:skipped]
|
|
89
|
-
|
|
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] ?
|
|
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 " #{
|
|
118
|
-
"#{
|
|
119
|
-
"#{
|
|
120
|
-
"#{
|
|
121
|
-
"#{
|
|
122
|
-
"#{
|
|
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
|
|
135
|
-
puts
|
|
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/
|
|
169
|
+
require_relative "../ai/services/table_generator"
|
|
174
170
|
require_relative "../terminal"
|
|
175
|
-
generator = AI::Services::
|
|
176
|
-
|
|
177
|
-
colors = ColorHelper
|
|
171
|
+
generator = AI::Services::TableGenerator.new
|
|
178
172
|
|
|
179
173
|
unless generator.ai_available?
|
|
180
|
-
puts
|
|
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
|
|
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
|
|
205
|
+
puts Output.format(:warning, " No changes - keeping current fields")
|
|
212
206
|
sleep 1
|
|
213
207
|
end
|
|
214
208
|
rescue => e
|
|
215
|
-
puts
|
|
209
|
+
puts Output.format(:error, "\n Error: #{e.message}")
|
|
216
210
|
sleep 2
|
|
217
211
|
end
|
|
218
212
|
end
|