strata-cli 0.1.15 → 0.1.16

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: 6965d8ee9c44a37f7de1a11c0d2654d05c44aa352f38442456e77814deb411e1
4
- data.tar.gz: af24ef90657a2c2d32d6493d00b1b40eab52a290c3f3c2c35fb69bf73cc7c448
3
+ metadata.gz: b01b5f82ca969d2d8e586722e10a438fefcc97e02107442d66d8de52bad55656
4
+ data.tar.gz: 8acfb5ff8b4ddd7aacb7de62f1f4f8758cb9b2004660422fcc2f3ee298bd46f6
5
5
  SHA512:
6
- metadata.gz: b9d03dcd92e8364ef2c5e3558c1f1e049227c394b36f75148f4f08e4d4b62ca9ddd3c11bd9e636c5c7324bba43e9d40f8ba554b7d4436fcb1ad4a07af3c001ee
7
- data.tar.gz: 455a627b5dfebd40b48f45ec70ea362438ed0c526db7c69941685987aea3a3fed10cf2c4a3b4fcaba33620e11dbda90540ce1c54de814bc74c10fac2cef36d98
6
+ metadata.gz: 7ce832a4ccb100cb4835ef212c67ee19ba207f51a33a8d36bc884102379827eb59b93f97cf9028a2dc742071a95006a8e361c840a6066c29dde3d01525240772
7
+ data.tar.gz: f7ce4fe653e46d6ccaf2b5945a08bcd866a0dc2bb3c43880e3facfca746c4f913750a04de1f7a5b51fe43c7c514f228d38bde62fc79d1013379cddeee194457c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.16] - 2026-09-05
4
+
5
+ ### Added
6
+
7
+ - **Local semantic audit**: `strata audit` now mirrors the server's deploy-time validations locally — SQL expression parsing (dimensions must reference a table column, measures need an aggregation function), cross-file `[Field]@d`/`@m` reference resolution, required field types and enum checks, table snapshot and partition rules, relationship join validation, and datasource adapter checks — so deploys fail fast with named errors instead of failing on the server.
8
+ - **Browser sign-in for deploy**: `strata deploy` now offers the browser-based auth flow when it needs an API key, with manual paste kept as a fallback.
9
+ - **Next steps guidance**: `strata init` and `strata datasource add` now print how to build the model next — manually with `strata create table` or via a coding agent — then audit and deploy.
10
+ - **Initial commit on init**: `strata init` commits the generated files (skipped when the repo already has history).
11
+
12
+ ### Changed
13
+
14
+ - **Agent mode auto-detection**: non-tty stdin now implies `--agent`, so agents get JSON output without discovering the flag. Prompts in agent mode emit an `interactive_required` JSON error and exit 1 instead of rendering an unanswerable TUI.
15
+ - **Deploy pre-checks**: the uncommitted-changes check now runs before any prompts, server calls, or audit.
16
+ - **AGENTS.md**: now allowlists which files agents may write (`models/**/*.yml`, `migrations/*.yml`, `tests/*.yml`) and points them to `strata datasource meta|exec --agent` instead of scratch scripts. `strata audit all` warns about stray non-YAML files under `models/` and scripts at the project root.
17
+
18
+ ### Fixed
19
+
20
+ - **Deploy rename detection**: the no-baseline fallback now includes all tracked YAML files (previously diffed only `HEAD~1`, dropping earlier renames), `--find-renames` is passed explicitly so rename records don't depend on local git config, and invalid baselines warn instead of silently degrading to a partial deploy.
21
+ - **Audit cardinality**: `many_to_many` is now rejected locally — the server enum rejects it with a crash.
22
+
3
23
  ## [0.1.15] - 2026-07-27
4
24
 
5
25
  ### Added
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tty-prompt"
3
4
  require_relative "agent_output"
4
5
 
5
6
  module Strata
@@ -10,10 +11,31 @@ module Strata
10
11
  desc: "Agent mode: structured output, no interactive prompts"
11
12
  end
12
13
 
14
+ # Implied when stdin is not a terminal. tty-prompt does not fail there --
15
+ # it redraws its menu forever on EOF -- so callers that cannot answer a
16
+ # prompt must be treated as agents whether or not they passed --agent.
13
17
  def agent_mode?
14
18
  value = options[:agent]
15
19
  value = options["agent"] if value.nil?
16
- value == true
20
+ value == true || !interactive?
21
+ end
22
+
23
+ def interactive?
24
+ $stdin.tty?
25
+ end
26
+
27
+ # Every interactive prompt goes through here so a non-interactive caller
28
+ # gets a JSON error it can read instead of a TUI it cannot answer.
29
+ def prompt
30
+ unless interactive?
31
+ AgentOutput.emit_error(
32
+ "This command needs an interactive terminal. Pass the required arguments, " \
33
+ "use --agent for structured output, or ask the user to run it.",
34
+ code: "interactive_required"
35
+ )
36
+ end
37
+
38
+ @prompt ||= TTY::Prompt.new
17
39
  end
18
40
 
19
41
  def reject_agent_mode!(message, code: "agent_mode_unsupported")
@@ -1,5 +1,9 @@
1
1
  Initialize a new Strata project or clone from an existing repository.
2
2
 
3
+ Creates an AGENTS.md in the project so a coding agent (Claude Code, Codex, Cursor)
4
+ can build the semantic model for you:
5
+ https://strata.do/developer-docs/developer-guide/cli/building-your-model
6
+
3
7
  PROJECT_NAME is optional when using --source option. If provided, creates a new project
4
8
  with that name. If using --source, clones the existing project from the URL.
5
9
 
@@ -6,6 +6,8 @@ require_relative "../helpers/project_helper"
6
6
  require_relative "../api/connection_error_handler"
7
7
  require_relative "../api/response_error_handler"
8
8
  require_relative "../output"
9
+ require_relative "../utils/git"
10
+ require_relative "../helpers/prompts"
9
11
  require "faraday"
10
12
  require "json"
11
13
  require "uri"
@@ -126,7 +128,22 @@ module Strata
126
128
  # Change into the project directory and run the existing add command
127
129
  inside(uid) do
128
130
  require_relative "../sub_commands/datasource"
129
- SubCommands::Datasource.new.add
131
+ # from_init: the datasource command skips its own next-steps block,
132
+ # completion_message prints one for the whole init instead.
133
+ SubCommands::Datasource.new([], {"from_init" => true}).add
134
+ end
135
+ end
136
+
137
+ def initial_commit
138
+ return if cloned_from_git?
139
+
140
+ inside uid do
141
+ case Utils::Git.initial_commit("chore: initialize Strata project")
142
+ when :committed
143
+ print_status(:created, "Initial git commit", type: :success)
144
+ when :failed
145
+ print_info("\n Could not create the initial commit — set git user.name and user.email, then commit yourself.")
146
+ end
130
147
  end
131
148
  end
132
149
 
@@ -134,9 +151,8 @@ module Strata
134
151
  Output.print_success("\n✔ Strata project '#{uid}' is ready!", context: self)
135
152
  Output.print_warning("\nNext steps:", context: self)
136
153
  Output.print_info(" 1. cd #{uid}", context: self)
137
- Output.print_info(" 2. strata datasource add # To add more datasources", context: self)
138
- Output.print_info(" 3. strata create table # Start adding tables", context: self)
139
- Output.print_info("\n", context: self)
154
+ Output.print_info(" 2. strata datasource add # add more datasources", context: self)
155
+ Output.print_info("\n#{Prompts.build_model_next_steps(uid)}", context: self)
140
156
  end
141
157
 
142
158
  private
@@ -93,6 +93,10 @@ Follow this sequence. Do not skip steps or reorder deploy before audit.
93
93
 
94
94
  **Agents must not run:** `strata deploy`, `strata datasource add`, interactive `strata create table` / `strata create relation` / `strata create migration`, or writing secrets into `.strata`.
95
95
 
96
+ **Agents may create or edit only:** `models/**/*.yml`, `migrations/*.yml`, `tests/*.yml`.
97
+
98
+ **Do not create any other file** — no `.rb` / `.py` / `.sh` scripts, notebooks, READMEs, or scratch validators. To inspect the warehouse use `strata datasource tables|meta|exec --agent`; to check your work use `strata audit all --agent`. `strata audit` warns about stray files. If you think a script is genuinely needed, stop and ask the user.
99
+
96
100
  **Branches:** Model on feature branches; deploy to the matching Strata server branch for staging. Production deploys typically use `production_branch` in `project.yml` (default `main`). Renames and swaps that affect production query names should happen on the production branch — see [Renames and swaps](#renames-swaps-and-production-branch).
97
101
 
98
102
  ## How the semantic layer maps to the warehouse
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "securerandom"
6
+ require "rbconfig"
7
+
8
+ module Strata
9
+ module CLI
10
+ module Helpers
11
+ # Browser-based sign-in: opens the server's CLI auth page and polls
12
+ # until the server hands back an API key for our one-time state token.
13
+ module BrowserAuth
14
+ AUTH_TIMEOUT_SECONDS = 300
15
+
16
+ def fetch_api_key_via_browser(server)
17
+ state = SecureRandom.hex(16)
18
+ auth_url = "#{server}/cli/auth?state=#{state}"
19
+ poll_url = "#{server}/api/v1/cli/auth/#{state}"
20
+
21
+ say "\nOpening your browser to sign in to Strata...", :cyan
22
+ open_browser(auth_url)
23
+ say "Waiting for authentication (you may close the browser tab once done)", :white
24
+
25
+ deadline = Time.now + AUTH_TIMEOUT_SECONDS
26
+ loop do
27
+ raise Strata::CommandError, "Authentication timed out. Please try again." if Time.now > deadline
28
+
29
+ begin
30
+ response = Net::HTTP.get_response(URI(poll_url))
31
+ return JSON.parse(response.body)["api_key"] if response.code == "200"
32
+ rescue Errno::ECONNREFUSED
33
+ raise Strata::CommandError, "Cannot reach server at #{server}. Is it running?"
34
+ end
35
+
36
+ print "."
37
+ $stdout.flush
38
+ sleep 1
39
+ end
40
+ end
41
+
42
+ def open_browser(url)
43
+ case RbConfig::CONFIG["host_os"]
44
+ when /darwin/ then system("open", url)
45
+ when /linux/ then system("xdg-open", url)
46
+ when /mswin|mingw/ then system("start", "", url)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -17,12 +17,8 @@ module Strata
17
17
  @adapter ||= create_adapter(datasource_key)
18
18
  end
19
19
 
20
- def prompt
21
- @prompt ||= TTY::Prompt.new
22
- end
23
-
24
20
  def datasource_key
25
- @datasource_key ||= resolve_datasource(prompt: prompt)
21
+ @datasource_key ||= resolve_datasource
26
22
  end
27
23
 
28
24
  def table_fetch_result
@@ -16,7 +16,7 @@ module Strata
16
16
  duckdb: %w[duckdb]
17
17
  }.freeze
18
18
 
19
- def resolve_datasource(ds_key_arg = nil, prompt: TTY::Prompt.new)
19
+ def resolve_datasource(ds_key_arg = nil)
20
20
  # 1. Use argument if provided
21
21
  return validate_datasource(ds_key_arg) if ds_key_arg
22
22
 
@@ -3,6 +3,27 @@
3
3
  module Strata
4
4
  module CLI
5
5
  module Prompts
6
+ # Docs. This URL is printed by the CLI and baked into every shipped gem —
7
+ # strata-docs has no redirects plugin, so renaming that page 404s old binaries.
8
+ DOCS_BUILD_MODEL_URL = "https://strata.do/developer-docs/developer-guide/cli/building-your-model"
9
+
10
+ # Shown after 'strata init' and 'strata datasource add'. %s is the agent hint.
11
+ MSG_AGENTS_MD_PRESENT = "AGENTS.md in this project tells it Strata's rules."
12
+ MSG_AGENTS_MD_MISSING = "See the guide below for what to tell it."
13
+ MSG_BUILD_MODEL_NEXT_STEPS = <<~STEPS
14
+ Build your semantic model — two ways:
15
+
16
+ • One table at a time: strata create table PATH/TABLE_NAME
17
+ • All at once: open this project in a coding agent (Claude Code,
18
+ Codex, Cursor) and ask it to build the semantic
19
+ model. %s
20
+
21
+ Then: strata audit all # validate
22
+ strata deploy # publish
23
+
24
+ Guide: #{DOCS_BUILD_MODEL_URL}
25
+ STEPS
26
+
6
27
  # Relation Command Prompts
7
28
  MSG_SELECT_LEFT_TABLE = "Select LEFT table (the 'many' side usually):"
8
29
  MSG_SELECT_RIGHT_TABLE_ALL = "Select RIGHT table (All):"
@@ -54,6 +75,12 @@ module Strata
54
75
  def default_migration_hook(operation)
55
76
  (operation == "swap") ? "post (after deployment)" : "pre (before deployment)"
56
77
  end
78
+
79
+ # dir is the project root — 'strata init --source' clones repos without an AGENTS.md.
80
+ def build_model_next_steps(dir = ".")
81
+ hint = File.exist?(File.join(dir, "AGENTS.md")) ? MSG_AGENTS_MD_PRESENT : MSG_AGENTS_MD_MISSING
82
+ format(MSG_BUILD_MODEL_NEXT_STEPS, hint)
83
+ end
57
84
  end
58
85
  end
59
86
  end