@altimateai/altimate-code 0.9.2 → 0.9.4

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.4] - 2026-07-31
9
+
10
+ Onboarding UX + first-run OAuth reliability. Ships the CLI's first-run scan + activation menu (Altimate LLM Gateway top of picker; bundled jaffle-shop DuckDB sample for users with no warehouse yet), then hardens the sign-in flow that path leads into.
11
+
12
+ ### Added
13
+
14
+ - **First-run scan + activation menu.** Fresh installs land on a curated 6-provider picker with Altimate LLM Gateway on top, then a Yes/No "Scan your environment?" gate that reads local config files (`.dbt/profiles.yml`, `dbt_project.yml`, `.git/config`) and routes into one of four branches — warehouse found, dbt-only, git-repo-no-dbt, nothing yet. The last branch offers to materialize a bundled jaffle-shop DuckDB sample dbt project (`~/altimate-sample-dbt/` by default; a suffixed variant if the name is taken) so a user with no warehouse can try Altimate against real data, real dbt models, without touching production. Every branch ends on a numbered "What would you like to do?" menu wired to real skills. (#1001)
15
+ - **Bundled jaffle-shop DuckDB sample project.** Ships with a pre-compiled `target/manifest.json` so `/discover` and `/review` work without dbt-core / dbt-duckdb installed. `dbt build` needs `pip install dbt-duckdb duckdb-cli` (or `pipx install --include-deps dbt-duckdb` / `uv tool install dbt-core --with dbt-duckdb` if you hit PEP 668). (#1001)
16
+
17
+ ### Fixed
18
+
19
+ - **Loopback OAuth sign-in is resilient to port collisions and long-SSO windows.** The Altimate LLM Gateway callback server now walks 7317-7325 on `EADDRINUSE` (a squatting dev tool on 7317 no longer wedges sign-in) and reports the actual bound port in the redirect. The pending-flow window extended from 5 min to 15 min so corporate SSO + MFA can finish. Two `authorize()` calls arriving concurrently now share one startup promise — both see the actually-bound port instead of one racing past on a stale/undefined value. (#1053)
20
+ - **CI test-isolation leak that turned six `permission/next.test.ts` tilde-expansion tests red.** `mcp/lifecycle.test.ts` was spying on `os.homedir()` in `beforeEach` with no `mockRestore`; the mock leaked forward into the next test file bun loaded, poisoning the tilde-expansion suite with a tmp path. Captured the spy handle and restored it in `afterEach`. Root-fix on the class, not the symptom. (#1053)
21
+ - **`HOME` recovery message is portable and doesn't tell users to write as root.** The prior text pointed at `/Users/you altimate-code` (macOS-only) and suggested `sudo HOME=…` which writes root-owned files a normal-user run then can't modify. Primary guidance is now "re-run without sudo" (install to a per-user prefix, or use nvm/asdf); secondary is `sudo -E HOME="$HOME" altimate-code` with an explicit `chown` recovery hint. (#1053)
22
+ - **PEP 668 install fallbacks actually expose a `dbt` binary.** `pipx install dbt-duckdb` and `uv tool install dbt-duckdb` both succeed but neither publishes the `dbt` entry point (it ships with `dbt-core`, a dependency). Users who followed the fallback after `pip` refused with `externally-managed-environment` hit a second dead end at their point of maximum frustration. Fixed to `pipx install --include-deps dbt-duckdb` and `uv tool install dbt-core --with dbt-duckdb`. The venv option also now asks for the absolute path — `~/.venvs/dbt/bin/dbt` wouldn't expand under the CLI's single-quoted validation. (#1053)
23
+ - **Scan disclosure is honest about telemetry.** The first-run scan's docs previously claimed "nothing leaves your computer". The scan itself doesn't send credentials, model contents, queries, or schema — but it does emit an anonymous environment summary (dbt-detected, warehouse-configured, etc.) through the standard telemetry pipeline if telemetry is enabled. Disclosure updated; `OPENCODE_DISABLE_TELEMETRY=1` remains the offline-strict switch. (#1053)
24
+ - **`sample_setup` error metadata no longer collides with success shape.** The unreachable `suffix: -1` sentinel was reverted to `0`; `success: false` is the disambiguator for metadata consumers, and the error `output` string separately begins `status: error` for the LLM template's failure branch. (#1053)
25
+ - **The auth plugin no longer leaks state-bearing authorize URLs into log files.** An earlier revision wrote the authorize URL to `process.stderr` as a fallback for SSH/tmux users; the auth plugin runs inside a TUI worker whose stdio is redirected to the log file (`packages/opencode/src/cli/tui/worker.ts`), so the write never reached the terminal it was written for. Removed. The TUI's auth dialog already renders the URL as a clickable link and binds `c` to copy it to the clipboard. (#1053)
26
+
27
+ ### Changed
28
+
29
+ - **`useConnected` and the home-tips visibility gate now share one predicate.** The "is any provider connected?" logic was duplicated in `component/use-connected.tsx` and `feature-plugins/home/tips.tsx`; both now call `isAnyProviderConnected` from `packages/tui/src/util/connected.ts` so a future edit to one can't drift them apart. Behavior unchanged (a semantically identical refactor). Non-`opencode` providers count as connected regardless of `cost` metadata (BYOK / self-hosted / custom-registration path); the `opencode` provider still requires a nonzero-cost model to count. (#1053)
30
+
31
+ ## [0.9.3] - 2026-07-24
32
+
33
+ Focused polish on the `altimate review` dbt PR reviewer — plus TUI startup UX
34
+ and audit-trail hardening on the signed verdict envelope.
35
+
36
+ ### Added
37
+
38
+ - **Grain-key `not_null` completeness detector.** For every column named in a `unique_combination_of_columns` test (or the `dbt_utils` equivalent), the reviewer now checks that a `not_null` test or constraint exists on the same column — without it, a NULL grain value silently passes the uniqueness guardrail. Supports contracted models (`constraints:` with `contract.enforced: true`), column-level `data_tests:` / `tests:`, and model-level primary-key constraints. Case-folds column names for Snowflake compatibility. Delivered +11 real findings on the internal 5-PR corpus. (#1029)
39
+ - **`--explain-tier` flag.** Pass `--explain-tier` to surface the classifier's tier-reason list on the verdict envelope so you can see why a diff was rated `trivial`, `lite`, or `full`. Full-tier verdicts also now include the reason line in the PR comment by default (no flag required), so a `REQUEST_CHANGES` on a schema.yml diff is never unexplained. (#1027)
40
+ - **`--force-tier` flag.** *[EXPERIMENTAL / bench debug]* Bypass the classifier with `--force-tier=trivial|lite|full`. The signed envelope carries `tierForced: true` and the classifier's original decision, so an auditor always sees the bypass — the flag can't be used silently. (#1027)
41
+ - **`riskTierPathTokens` config in `.altimate/review.yml`.** Configure named token categories (including `preset:finops` for the built-in cost/billing keyword list) to promote matching files to full review tier. Previously the FinOps preset was hardcoded and always on; it is now opt-in via this config. A typo in a category value surfaces in both stderr and the verdict envelope `tierReasons[]` (and the PR comment) so it can't kill your opt-in silently. (#1028)
42
+ - **`engine.cliVersion` in the signed verdict envelope.** The altimate-code release that generated each verdict is now stamped into `engine.cliVersion` and covered by the HMAC signature — auditors reconstructing decisions from stored envelopes months later can identify which policy version applied.
43
+ - **`staleManifest` flag on the verdict envelope.** When a change-affecting source file has been modified after the manifest was written, the signed envelope now carries `staleManifest: true`. A stderr warning alone is easy for CI to swallow; the envelope field is durable and part of the signed body.
44
+ - **Bootstrap phase labels in the TUI.** While the agent initializes, the busy spinner now shows what it is actually doing ("Loading config…", "Discovering tools…", "Thinking…") matching the convention users already know from other agent UIs. Phase events are also emitted as trace spans for server-side latency profiling. (#1002)
45
+
46
+ ### Changed
47
+
48
+ - **Risky dbt metadata surfaces never auto-approve.** PRs that touch `data_tests:`, `tests:`, `constraints:`, `contract:`, or `unique_combination_of_columns` in schema.yml under `models/marts/` — or files under any configured `riskTierPathTokens` category — are always promoted to `full` review tier. A small metadata diff can no longer slip through on `trivial` classification. Grounded in a corpus study where two PRs auto-approved with 8–11 human-visible findings each. **CI in `gate` mode may see exit-code changes on affected PRs.** (#1028)
49
+ - **`--no-ai` flag now actually disables the LLM lane.** A yargs `boolean-negation` conflict caused a bare `--no-ai` to silently fall into the help path (exit 0, no review ran). The flag is fixed; CI scripts using `--no-ai` since v0.8.x should verify behavior on upgrade — those runs may have been executing the AI lane and getting billed. (#1027)
50
+ - **`altimate review` auto-discovers `target/manifest.json`.** When `--manifest` is not passed and the config-relative path doesn't resolve, the reviewer walks up from `cwd` to the nearest `dbt_project.yml` and uses its adjacent `target/manifest.json`. Discovery is logged to stderr; explicit `--manifest` still wins. Removes the most common "review did nothing" support case for mono-repo invocations. (#1027)
51
+ - **schema.yml test-removal detection is now column-aware.** The detector parses both sides of the diff structurally (YAML parse rather than diff-line pattern) and keys removals by `(model, column, test)` — eliminating false cancellations where a sibling column re-added the same test type. (#1027)
52
+ - **Stale-manifest detection now runs for local working-tree reviews too.** Previously gated behind `--head`, so the local workflow "`dbt compile` once, edit for an hour, then `altimate review`" — where staleness bites in practice — silently under-warned. The check is limited to files that could materially change the manifest (models, schema.yml, seeds, macros, dbt config) so noise stays bounded.
53
+
8
54
  ## [0.9.2] - 2026-07-20
9
55
 
10
56
  ### Added
package/package.json CHANGED
@@ -7,20 +7,20 @@
7
7
  "scripts": {
8
8
  "postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
9
9
  },
10
- "version": "0.9.2",
10
+ "version": "0.9.4",
11
11
  "license": "MIT",
12
12
  "dependencies": {
13
13
  "@altimateai/altimate-core": "0.5.1"
14
14
  },
15
15
  "optionalDependencies": {
16
- "@altimateai/altimate-code-linux-arm64": "0.9.2",
17
- "@altimateai/altimate-code-linux-x64-baseline": "0.9.2",
18
- "@altimateai/altimate-code-linux-x64": "0.9.2",
19
- "@altimateai/altimate-code-windows-x64": "0.9.2",
20
- "@altimateai/altimate-code-darwin-x64-baseline": "0.9.2",
21
- "@altimateai/altimate-code-darwin-arm64": "0.9.2",
22
- "@altimateai/altimate-code-darwin-x64": "0.9.2",
23
- "@altimateai/altimate-code-windows-x64-baseline": "0.9.2"
16
+ "@altimateai/altimate-code-darwin-x64-baseline": "0.9.4",
17
+ "@altimateai/altimate-code-linux-arm64": "0.9.4",
18
+ "@altimateai/altimate-code-darwin-x64": "0.9.4",
19
+ "@altimateai/altimate-code-windows-x64-baseline": "0.9.4",
20
+ "@altimateai/altimate-code-linux-x64": "0.9.4",
21
+ "@altimateai/altimate-code-linux-x64-baseline": "0.9.4",
22
+ "@altimateai/altimate-code-darwin-arm64": "0.9.4",
23
+ "@altimateai/altimate-code-windows-x64": "0.9.4"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "pg": ">=8",
@@ -0,0 +1,81 @@
1
+ # Jaffle Shop — altimate-code starter sample
2
+
3
+ Everything below runs against a local DuckDB file — no cloud warehouse, no
4
+ credentials, no network calls.
5
+
6
+ ## What's in here
7
+
8
+ ```
9
+ dbt_project.yml dbt project config
10
+ profiles.yml DuckDB profile — path is project-relative
11
+ sample-manifest.json version metadata used by altimate-code to detect
12
+ stale copies on upgrade
13
+ models/
14
+ staging/
15
+ stg_customers.sql renames raw customer columns to snake_case
16
+ stg_orders.sql renames raw order columns
17
+ schema.yml column descriptions + unique/not_null tests
18
+ marts/
19
+ customers.sql one row per customer, joins in order counts
20
+ orders.sql one row per order, joins in customer names
21
+ schema.yml column descriptions + tests + relationships
22
+ seeds/
23
+ raw_customers.csv 3 rows of test data
24
+ raw_orders.csv 4 rows of test data
25
+ target/
26
+ manifest.json PRE-COMPILED dbt manifest — ships with the sample so
27
+ altimate-code's static workflows (/discover, /review)
28
+ work without dbt-core / dbt-duckdb installed
29
+ ```
30
+
31
+ ## What to try
32
+
33
+ **If you got here via altimate-code's activation menu**, the chat is already
34
+ offering you a numbered menu — pick a number (or say what else you want to
35
+ do). Every option is wired to a real workflow:
36
+
37
+ - **See what breaks downstream before you change a model** — try `customers`
38
+ or `orders`.
39
+ - **Review the SQL in this project with every finding explained** — targets
40
+ the mart models.
41
+ - **Build & query it** — altimate-code detects whether dbt-core + dbt-duckdb
42
+ are installed, offers to reuse an existing dbt binary if you paste its
43
+ path, and walks you through the run. Auto-registers the DuckDB file as a
44
+ warehouse so `sql_execute` connects to the database dbt just built.
45
+
46
+ **If you `cd`'d into `~/altimate-sample-dbt/` from a different shell** — no
47
+ activation menu, no LLM in the loop. You can drive dbt yourself:
48
+
49
+ ```bash
50
+ # Three PyPI packages that do different jobs:
51
+ # - dbt-core: provides the `dbt` binary (the entry point)
52
+ # - dbt-duckdb: adapter — teaches dbt-core how to talk to DuckDB
53
+ # - duckdb-cli: standalone `duckdb` binary the ad-hoc query line uses
54
+ # `pip install dbt-duckdb` pulls dbt-core as a dependency, so pip is fine.
55
+ # `pipx install dbt-duckdb` does NOT — pipx only exposes entry points from the
56
+ # named package, and dbt-duckdb has none. Use `--include-deps` if you go pipx.
57
+ pip install dbt-duckdb duckdb-cli # PEP 668 alternatives below
58
+ cd ~/altimate-sample-dbt # wherever you materialized the sample
59
+ dbt seed # load the CSVs into DuckDB
60
+ dbt build # run models + tests
61
+ duckdb target/jaffle.duckdb -c 'select * from customers'
62
+ ```
63
+
64
+ If plain `pip` fails with `externally-managed-environment` (PEP 668, common
65
+ on modern macOS Homebrew and Debian/Ubuntu Python), use one of these instead:
66
+
67
+ ```bash
68
+ pipx install --include-deps dbt-duckdb && pipx install duckdb-cli
69
+ # or
70
+ uv tool install dbt-core --with dbt-duckdb && uv tool install duckdb-cli
71
+ ```
72
+
73
+ Once `dbt` is discoverable (venv, pipx, conda, uv, poetry, homebrew — the CLI
74
+ walks every common Python env manager), altimate-code's "Build & query"
75
+ option surfaces the same actions inside the chat.
76
+
77
+ ## Bringing your own project
78
+
79
+ When you're ready to switch to your real dbt project, `cd` into it and run
80
+ altimate-code again. The scan will pick up your `dbt_project.yml` and offer
81
+ to connect its warehouse.
@@ -0,0 +1,16 @@
1
+ name: "jaffle_shop"
2
+ version: "1.0.0"
3
+
4
+ profile: "jaffle_shop"
5
+
6
+ model-paths: ["models"]
7
+ seed-paths: ["seeds"]
8
+ target-path: "target"
9
+ clean-targets: ["target", "dbt_packages"]
10
+
11
+ models:
12
+ jaffle_shop:
13
+ staging:
14
+ +materialized: view
15
+ marts:
16
+ +materialized: table
@@ -0,0 +1,9 @@
1
+ select
2
+ c.customer_id,
3
+ c.first_name,
4
+ c.last_name,
5
+ count(o.order_id) as order_count,
6
+ coalesce(sum(o.amount), 0) as total_amount
7
+ from {{ ref('stg_customers') }} c
8
+ left join {{ ref('stg_orders') }} o on c.customer_id = o.customer_id
9
+ group by c.customer_id, c.first_name, c.last_name
@@ -0,0 +1,8 @@
1
+ select
2
+ o.order_id,
3
+ o.customer_id,
4
+ c.first_name || ' ' || c.last_name as customer_name,
5
+ o.order_date,
6
+ o.amount
7
+ from {{ ref('stg_orders') }} o
8
+ join {{ ref('stg_customers') }} c on o.customer_id = c.customer_id
@@ -0,0 +1,32 @@
1
+ version: 2
2
+
3
+ models:
4
+ - name: customers
5
+ description: One row per customer with total order count and revenue.
6
+ columns:
7
+ - name: customer_id
8
+ description: Primary key.
9
+ data_tests:
10
+ - unique
11
+ - not_null
12
+ - name: order_count
13
+ description: Number of orders placed by the customer (0 when none).
14
+ data_tests:
15
+ - not_null
16
+ - name: total_amount
17
+ description: Sum of all order amounts (0 when none).
18
+ data_tests:
19
+ - not_null
20
+
21
+ - name: orders
22
+ description: One row per order with a joined customer name.
23
+ columns:
24
+ - name: order_id
25
+ description: Primary key.
26
+ data_tests:
27
+ - unique
28
+ - not_null
29
+ - name: customer_id
30
+ description: Foreign key to `stg_customers`.
31
+ data_tests:
32
+ - not_null
@@ -0,0 +1,27 @@
1
+ version: 2
2
+
3
+ models:
4
+ - name: stg_customers
5
+ description: Renamed customer columns from the raw seed.
6
+ columns:
7
+ - name: customer_id
8
+ description: Primary key of the customer.
9
+ data_tests:
10
+ - unique
11
+ - not_null
12
+
13
+ - name: stg_orders
14
+ description: Renamed order columns from the raw seed.
15
+ columns:
16
+ - name: order_id
17
+ description: Primary key of the order.
18
+ data_tests:
19
+ - unique
20
+ - not_null
21
+ - name: customer_id
22
+ description: Foreign key to `stg_customers`.
23
+ data_tests:
24
+ - not_null
25
+ - relationships:
26
+ to: ref('stg_customers')
27
+ field: customer_id
@@ -0,0 +1,5 @@
1
+ select
2
+ id as customer_id,
3
+ first_name,
4
+ last_name
5
+ from {{ ref('raw_customers') }}
@@ -0,0 +1,6 @@
1
+ select
2
+ id as order_id,
3
+ customer_id,
4
+ order_date,
5
+ amount
6
+ from {{ ref('raw_orders') }}
@@ -0,0 +1,14 @@
1
+ # DuckDB profile — everything runs locally against a single file at
2
+ # `target/jaffle.duckdb` (created on first `dbt build`). No cloud credentials.
3
+ # `path:` is unqualified, so dbt-duckdb resolves it against the PROCESS
4
+ # working directory at build time — NOT the project directory. Run
5
+ # `dbt build` from the materialized sample dir (`cd <sample-path>`) and
6
+ # the database lands at `<sample-path>/target/jaffle.duckdb`.
7
+ # Run it from anywhere else and dbt writes to `$PWD/target/jaffle.duckdb`.
8
+ jaffle_shop:
9
+ target: dev
10
+ outputs:
11
+ dev:
12
+ type: duckdb
13
+ path: "target/jaffle.duckdb"
14
+ threads: 1
@@ -0,0 +1,27 @@
1
+ {
2
+ "$comment": "Metadata about this sample project. Distinct from dbt's target/manifest.json — this is our own version stamp for conflict detection on the materialized copy at ~/altimate-sample-dbt/. If you edit any source file below, run ./regenerate.sh and commit the refreshed target/manifest.json alongside your change.",
3
+ "name": "jaffle-shop-duckdb",
4
+ "version": "1.0.0",
5
+ "kind": "altimate-starter-sample",
6
+ "source": "packages/opencode/sample-projects/jaffle-shop-duckdb",
7
+ "requires": {
8
+ "dbt-core": ">=1.7 <2.0",
9
+ "dbt-duckdb": ">=1.7 <2.0"
10
+ },
11
+ "notes": [
12
+ "Renamed profile from the original test fixture: `test_jaffle_shop` → `jaffle_shop`.",
13
+ "DuckDB target file resolves project-relative at `target/jaffle.duckdb`; no host paths bake into the profile.",
14
+ "target/manifest.json ships pre-compiled so static workflows (/discover, /review) work without dbt installed."
15
+ ],
16
+ "$assets_comment": "Single source of truth for the file list shipped to end users. `packages/opencode/src/altimate/onboarding/materialize.ts` copies these into the user's home; `packages/opencode/script/publish.ts` copies the same list into the wrapper npm package at release time; `packages/opencode/test/altimate/onboarding/publish-parity.test.ts` cross-checks both consumers use the same list. Adding a new sample file? Add it here — everything else picks it up automatically.",
17
+ "assets": [
18
+ { "from": "README.md", "kind": "file", "required": true },
19
+ { "from": "dbt_project.yml", "kind": "file", "required": true },
20
+ { "from": "profiles.yml", "kind": "file", "required": true },
21
+ { "from": "sample-manifest.json", "kind": "file", "required": true },
22
+ { "from": ".gitignore", "kind": "file", "required": false },
23
+ { "from": "models", "kind": "dir", "required": true },
24
+ { "from": "seeds", "kind": "dir", "required": true },
25
+ { "from": "target/manifest.json", "kind": "file", "required": true }
26
+ ]
27
+ }
@@ -0,0 +1,4 @@
1
+ id,first_name,last_name
2
+ 1,Alice,Smith
3
+ 2,Bob,Jones
4
+ 3,Carol,White
@@ -0,0 +1,5 @@
1
+ id,customer_id,order_date,amount
2
+ 1,1,2024-01-15,100
3
+ 2,1,2024-02-20,200
4
+ 3,2,2024-01-10,150
5
+ 4,3,2024-03-05,300