strata-cli 0.1.14 → 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: 7b0fdc6cf6a28080152c9b0237d54e206740477d196178a2e6d5aa5642720135
4
- data.tar.gz: d049c71dd05982f8a52b786ae59ffdf590056dc3bdb5cadd7045227dbe9de867
3
+ metadata.gz: b01b5f82ca969d2d8e586722e10a438fefcc97e02107442d66d8de52bad55656
4
+ data.tar.gz: 8acfb5ff8b4ddd7aacb7de62f1f4f8758cb9b2004660422fcc2f3ee298bd46f6
5
5
  SHA512:
6
- metadata.gz: f7c7aa4945d149bff16578ca52f99a005b3c9d4c3b5897b52929bf9bfffb140883457f60d52552fa551faee5ef6d0472069c368125653bf784078378a7065dbe
7
- data.tar.gz: 48a81467d16e8b489bdce4f177dd1b158ac09344c07d1944d804a679afc0ed47e52047b6a478781ba245c291a4d9bb8512f8159b1d09ecf41e1f537b5873e52b
6
+ metadata.gz: 7ce832a4ccb100cb4835ef212c67ee19ba207f51a33a8d36bc884102379827eb59b93f97cf9028a2dc742071a95006a8e361c840a6066c29dde3d01525240772
7
+ data.tar.gz: f7ce4fe653e46d6ccaf2b5945a08bcd866a0dc2bb3c43880e3facfca746c4f913750a04de1f7a5b51fe43c7c514f228d38bde62fc79d1013379cddeee194457c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
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
+
23
+ ## [0.1.15] - 2026-07-27
24
+
25
+ ### Added
26
+
27
+ - **Sample project command**: `strata project sample` now uses a browser-based auth flow — opens the Strata sign-in page, waits for authentication, and writes the API key to `.strata` automatically. No flags required.
28
+ - **CLI auth endpoints**: New server endpoints (`/cli/auth` and `/api/v1/cli/auth/:state`) support the browser-based API key retrieval flow.
29
+
30
+ ### Changed
31
+
32
+ - **Repo rename**: Sample project now clones `tpc-ds-retail-sample` (previously `strata-tutorial-project`).
33
+ - **Sample project setup**: Server URL is prompted interactively with `http://localhost:8080` as default. `--api-key` and `--server` flags retained as optional overrides for scripting.
34
+ - **Audit — tutorial project**: Connection check now silently passes for the `tpc-ds-retail-sample` project (datasource lives in the Strata Docker container, not locally). All other audit checks run normally.
35
+ - **Project template**: `project.yml` no longer assumes `http://localhost:8080` as the default server — prompted at first deploy instead.
36
+
37
+ ### Fixed
38
+
39
+ - **Deploy — manual commit hash**: Deployments previously triggered via the web UI stored a `manual-<timestamp>` commit reference. The CLI now treats these as non-git baselines and falls back to a full deploy instead of erroring.
40
+
3
41
  ## [0.1.14] - 2026-07-17
4
42
 
5
43
  ### Added
data/README.md CHANGED
@@ -308,18 +308,27 @@ project_id: 123 # Auto-populated after first deployment
308
308
 
309
309
  **Multi-Environment Configuration:**
310
310
 
311
+ `project.yml` — server URL per environment (safe to commit):
311
312
  ```yaml
312
- # Default environment
313
- server: http://localhost:3000
313
+ # Default environment (set on first deploy if omitted)
314
+ server: https://dev.strata.example.com
314
315
 
315
- # Environment-specific configs
316
316
  staging:
317
317
  server: https://staging.strata.com
318
- api_key: staging-key
319
318
 
320
319
  production:
321
320
  server: https://app.strata.com
322
- api_key: prod-key
321
+ ```
322
+
323
+ `.strata` — API keys per environment (**never committed**):
324
+ ```yaml
325
+ api_key: your-default-api-key
326
+
327
+ staging:
328
+ api_key: your-staging-api-key
329
+
330
+ production:
331
+ api_key: your-production-api-key
323
332
  ```
324
333
 
325
334
  ### Local Configuration (`.strata`)
@@ -436,7 +445,7 @@ Ensure you have write permissions in the project directory. The `.strata` file p
436
445
 
437
446
  ### "Server URL not configured"
438
447
 
439
- Add the `server` field to your `project.yml`:
448
+ Run `strata deploy` — you will be prompted to enter the server URL, which is then saved to `project.yml` automatically for future deploys. Or add it manually:
440
449
 
441
450
  ```yaml
442
451
  server: https://your-strata-server.com
@@ -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"
@@ -17,8 +19,6 @@ module Strata
17
19
  include API::ConnectionErrorHandler
18
20
  include API::ResponseErrorHandler
19
21
 
20
- BASE_SERVER_URL = "http://localhost:3000"
21
-
22
22
  argument :name, type: :string, required: false, desc: "The name of the project. Optional when using --source."
23
23
  class_option :datasource, type: :string, repeatable: true
24
24
  class_option :source, type: :string
@@ -128,7 +128,22 @@ module Strata
128
128
  # Change into the project directory and run the existing add command
129
129
  inside(uid) do
130
130
  require_relative "../sub_commands/datasource"
131
- 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
132
147
  end
133
148
  end
134
149
 
@@ -136,9 +151,8 @@ module Strata
136
151
  Output.print_success("\n✔ Strata project '#{uid}' is ready!", context: self)
137
152
  Output.print_warning("\nNext steps:", context: self)
138
153
  Output.print_info(" 1. cd #{uid}", context: self)
139
- Output.print_info(" 2. strata datasource add # To add more datasources", context: self)
140
- Output.print_info(" 3. strata create table # Start adding tables", context: self)
141
- 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)
142
156
  end
143
157
 
144
158
  private
@@ -205,12 +219,6 @@ module Strata
205
219
  @name
206
220
  end
207
221
 
208
- def server_url
209
- return options[:source] if options.key?(:source)
210
-
211
- BASE_SERVER_URL
212
- end
213
-
214
222
  def description
215
223
  return @project_info["description"] if options.key?(:source) && @project_info
216
224
 
@@ -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
@@ -10,14 +10,14 @@ uid: <%= uid %>
10
10
  description: Your projects description
11
11
 
12
12
  # Target Strata server (default environment, typically dev)
13
- server: <%= options[:source] || "http://localhost:3000" %>
13
+ server: <%= options[:source] %>
14
14
 
15
15
  # Alternatively, you can configure multiple environments (different Strata server instances).
16
16
  # Each environment represents a separate Strata cluster (dev, staging, production).
17
17
  # Use -e [ENVIRONMENT] flag when deploying to select the environment.
18
18
  # Example:
19
19
  # dev:
20
- # server: http://localhost:3000
20
+ # server: http://localhost:8080
21
21
  # prod:
22
22
  # server: https://app.strata.com
23
23
 
@@ -14,6 +14,8 @@ module Strata
14
14
  version
15
15
  deploy
16
16
  branch
17
+ project
18
+ sample
17
19
  ].freeze
18
20
 
19
21
  def invoke_command(command, *args)
@@ -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