Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
79ede24
test: [v0.9.4-a1] restore os.homedir spy in mcp lifecycle test to unb…
Jul 30, 2026
6ca01ba
fix: [v0.9.4-a2] materialize: quote the correct recovery command when…
Jul 30, 2026
739c105
fix: [v0.9.4-a3+a4] loopback OAuth: walk port 7317-7325, 15-min windo…
Jul 30, 2026
bee9109
refactor: [v0.9.4-a6] useConnected: explicit provider-id branch + gra…
Jul 30, 2026
d6c389a
docs(template): [v0.9.4-a7+a8] surface sample path + PEP 668 alternat…
Jul 30, 2026
89f3946
docs: [v0.9.4-a9] quickstart-new: welcome picker, first-run scan, act…
Jul 30, 2026
a77a05a
docs(sample): [v0.9.4-a10] jaffle-shop README: point activation-menu …
Jul 30, 2026
bc28066
fix: [v0.9.4-a11] sample_setup: error metadata uses suffix=-1 so shap…
Jul 30, 2026
a8d3294
docs(util): [v0.9.4-a12] writeJsonAtomic docstring notes migration is…
Jul 30, 2026
0c91602
test: [v0.9.4-flake] delete phase-label TUI e2e test (flaky locally, …
Jul 31, 2026
2cbc1e8
test(acp): [v0.9.4-ci] env-hermetic guard against ALTIMATE_CLI_YOLO i…
Jul 31, 2026
f9f13bd
test(install): [v0.9.4-ci] skip smoke-test-binary when local build is…
Jul 31, 2026
6c20407
test(cli-process): [v0.9.4-ci] retry once on SQLite "database is lock…
Jul 31, 2026
f75a710
fix(auth): [v0.9.4-bot-B1] atomic callback-server startup for concurr…
Jul 31, 2026
f9581d9
fix(materialize): [v0.9.4-bot-B2] portable + ownership-safe HOME reco…
Jul 31, 2026
1221911
docs: [v0.9.4-bot] absolute dbt path, telemetry disclosure, #1052 lin…
Jul 31, 2026
f5ec426
test: [v0.9.4-bot] hoist mkdtemp out of homedir mock; share retry tim…
Jul 31, 2026
7cd019e
docs: [v0.9.4-consensus-M1+n1] pipx/uv install expose dbt binary; qua…
Jul 31, 2026
653f72b
fix(auth): [v0.9.4-consensus-M2+m2+m10] drop dead stderr echo; correc…
Jul 31, 2026
790df9b
refactor(tui): [v0.9.4-consensus-m7+m8] extract shared connected pred…
Jul 31, 2026
4caaee0
test+tool: [v0.9.4-consensus-m3+m5+m6] observable retry; wider stalen…
Jul 31, 2026
9b5d3a3
fix(tui): [v0.9.4-marker-guard] wrap tips.tsx altimate_change comment…
Jul 31, 2026
e82faf5
fix(tui): [v0.9.4-branding] util/connected.ts drops OpenCode product-…
Jul 31, 2026
fa72084
docs: [v0.9.4] add CHANGELOG entry
Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.9.4] - 2026-07-31

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.

### Added

- **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)
- **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)

### Fixed

- **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)
- **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)
- **`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)
- **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)
- **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)
- **`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)
- **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)

### Changed

- **`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)

## [0.9.3] - 2026-07-24

Focused polish on the `altimate review` dbt PR reviewer — plus TUI startup UX
Expand Down
29 changes: 25 additions & 4 deletions docs/docs/getting-started/quickstart-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,43 @@ npm install -g altimate-code

---

## Step 2: Connect Your LLM
## Step 2: Sign in

```bash
altimate # Launch the TUI
/connect # Interactive setup
```

Or set an environment variable and skip the prompt:
On a fresh install, a welcome panel appears with a curated 6-provider picker:

- **Altimate LLM Gateway** *(recommended)* — 10M tokens free, no API keys. Routes to the best model per task across Sonnet, Opus, GPT-5, and more. Sign-in opens a browser tab; complete Google or email signup and you're back in the TUI. If your terminal can't open a browser (SSH / tmux / WSL), the CLI prints the URL — paste it into a browser on your desktop.
- **Anthropic** / **OpenAI** / **Google** — paste an API key or OAuth in.
- **Big Pickle** — free tier, chats work but many data tasks fail; useful for kicking tires.
- **Search all providers…** — full picker if you need Bedrock, Databricks AI Gateway, Cloudflare AI Gateway, Snowflake Cortex, DigitalOcean Inference, etc.

Or set an environment variable and skip the picker:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
altimate
```

!!! tip "Don't want to manage API keys?"
The [Altimate LLM Gateway](https://help.altimate.ai/datamates/user-guide/components/llm-gateway/) gives you 10M tokens free — no API keys needed. It dynamically routes to the best model for each task across Sonnet 4.6, Opus 4.6, GPT-5.4, and more.
The [Altimate LLM Gateway](https://help.altimate.ai/datamates/user-guide/components/llm-gateway/) is the top row of the picker — 10M free tokens, and altimate-code auto-selects the right model per task. First-run sign-in uses a loopback OAuth on `127.0.0.1:7317-7325` (falls back if the preferred port is taken).

---

## Step 2.5: First-run scan (optional)

Immediately after model setup, a **"Scan your environment?"** Yes/No dialog appears. Say **Yes** and altimate-code reads local config files (`.dbt/profiles.yml`, `dbt_project.yml`, `.git/config`) — no credentials are read or sent, and no schema, model contents, or queries leave your computer. An anonymous environment summary (e.g. "dbt project detected, no warehouse configured") may be included in the standard telemetry stream if telemetry is enabled; disable via `OPENCODE_DISABLE_TELEMETRY=1` or the [telemetry docs](../usage/telemetry.md) if you want a strictly-offline scan. The scan then routes you into one of four branches:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 \
  'profiles\.yml|OPENCODE_DISABLE_TELEMETRY|telemetry|environment summary|credential|secret|password|token' \
  packages/opencode packages/tui docs

Repository: AltimateAI/altimate-code

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked matching files:\n'
git ls-files 'packages/opencode/**' 'packages/tui/**' 'docs/**' \
  | rg 'profiles\.yml|telemetry|scan|config|packages/opencode|packages/tui|docs/docs/getting-started/quickstart-new.md|usage/telemetry.md' \
  | head -200

printf '\nCandidate symbols:\n'
rg -n -C 4 \
  'profile|profiles\.yml|target-path|catalog|warehouse|host|password|token|pass|credential|secret|telemetry|OPENCODE_DISABLE_TELEMETRY|environment summary' \
  packages/opencode packages/tui docs/docs docs/usage 2>/dev/null \
  | head -250

Repository: AltimateAI/altimate-code

Length of output: 9810


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'File stats and quickstart context:\n'
wc -l docs/docs/getting-started/quickstart-new.md
sed -n '30,55p' docs/docs/getting-started/quickstart-new.md

printf '\nLocate exact mentions in docs only (narrow):\n'
rg -n -C 5 \
  'profiles\.yml|OPENCODE_DISABLE_TELEMETRY|telemetry|environment summary|credential|credential fields|secret fields|password|token' \
  docs/docs docs/usage 2>/dev/null | head -200

printf '\nFind likely telemetry files:\n'
git ls-files 'packages/opencode/**' 'packages/tui/**' | rg -i 'telem|sentinel|config|scanner|scan|profile|dbt' | head -200

Repository: AltimateAI/altimate-code

Length of output: 17431


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'dbt profiles registry:\n'
wc -l packages/opencode/src/altimate/native/connections/dbt-profiles.ts
sed -n '1,260p' packages/opencode/src/altimate/native/connections/dbt-profiles.ts

printf '\nTelemetry core:\n'
wc -l packages/opencode/src/altimate/telemetry/index.ts
sed -n '1,260p' packages/opencode/src/altimate/telemetry/index.ts
rg -n -C 6 \
  'OPENCODE_DISABLE_TELEMETRY|telemetry|EnvironmentSummary|profiles|dbt_project|git/config|credential|secret|password|environmentSummary|summary|redact' \
  packages/opencode/src/altimate packages/opencode/src/altimate/telemetry.ts packages/opencode/src 2>/dev/null \
  | head -350

Repository: AltimateAI/altimate-code

Length of output: 46580


Document collection as skipped before transmitting.

Line 44 claims credentials are not read or sent. profiles.yml parsing retains mapped credential fields such as token/private-key fields, so say credentials are not collected or transmitted and update the code to exclude them before serialization if the scan sends telemetry summaries.

🧰 Tools
🪛 LanguageTool

[grammar] ~44-~44: Ensure spelling is correct
Context: ... Yes/No dialog appears. Say Yes and altimate-code reads local config files (`.dbt/pro...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/getting-started/quickstart-new.md` at line 44, Update the scan
documentation to state that credentials are not collected or transmitted, rather
than claiming they are not read. In the scan’s telemetry-summary serialization
flow, remove mapped credential fields such as tokens and private keys before any
summary is transmitted, while preserving the non-sensitive environment summary.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The [telemetry docs](../usage/telemetry.md) link on line 44 of quickstart-new.md resolves to usage/telemetry.md which does not exist (docs/docs/usage/telemetry.md not found). The actual Telemetry page lives at reference/telemetry.md. Visitors clicking this link from the published docs will hit a 404. Update the relative path to ../reference/telemetry.md to point to the existing Telemetry reference page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/docs/getting-started/quickstart-new.md, line 44:

<comment>The `[telemetry docs](../usage/telemetry.md)` link on line 44 of quickstart-new.md resolves to `usage/telemetry.md` which does not exist (`docs/docs/usage/telemetry.md` not found). The actual Telemetry page lives at `reference/telemetry.md`. Visitors clicking this link from the published docs will hit a 404. Update the relative path to `../reference/telemetry.md` to point to the existing Telemetry reference page.</comment>

<file context>
@@ -41,7 +41,7 @@ altimate
 ## Step 2.5: First-run scan (optional)
 
-Immediately after model setup, a **"Scan your environment?"** Yes/No dialog appears. Say **Yes** and altimate-code reads local config files (no credentials sent, nothing leaves your computer) and routes you into one of four branches:
+Immediately after model setup, a **"Scan your environment?"** Yes/No dialog appears. Say **Yes** and altimate-code reads local config files (`.dbt/profiles.yml`, `dbt_project.yml`, `.git/config`) — no credentials are read or sent, and no schema, model contents, or queries leave your computer. An anonymous environment summary (e.g. "dbt project detected, no warehouse configured") may be included in the standard telemetry stream if telemetry is enabled; disable via `OPENCODE_DISABLE_TELEMETRY=1` or the [telemetry docs](../usage/telemetry.md) if you want a strictly-offline scan. The scan then routes you into one of four branches:
 
 - **Found a warehouse** → offers to add + verify each connection, then index its schema.
</file context>


- **Found a warehouse** → offers to add + verify each connection, then index its schema.
- **Found dbt project, no warehouse** → asks which warehouse it runs against and walks you through `warehouse_add`.
- **In a git repo, no dbt** → suggests you `cd` into the right project and re-run.
- **Nothing yet** → offers to try Altimate on a sample dbt project (bundled jaffle-shop DuckDB, no warehouse needed) or another exploratory job.

Every branch ends on a numbered "**What would you like to do?**" menu in the chat — pick a job by typing the number, or free-text if none fit. **The menu is chat text, not an arrow-key picker; type your answer and press Enter.**

Say **No** to the scan gate and you land on the same activation menu without the scan detail — good for users who already know what they want to run.

---

Expand Down
50 changes: 38 additions & 12 deletions packages/opencode/sample-projects/jaffle-shop-duckdb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,51 @@ target/
work without dbt-core / dbt-duckdb installed
```

## What to try (works with zero external tools)
## What to try

- `/discover stg_customers` — walk the DAG and see what depends on this model
- `/review models/marts/customers.sql` — run the reviewer against a mart model
- Open any `.sql` file and ask altimate-code to explain the transformation
- Ask altimate-code "what tests would you recommend for `orders`?"
**If you got here via altimate-code's activation menu**, the chat is already
offering you a numbered menu — pick a number (or say what else you want to
do). Every option is wired to a real workflow:

## What to try (needs `dbt-core` + `dbt-duckdb` installed)
- **See what breaks downstream before you change a model** — try `customers`
or `orders`.
- **Review the SQL in this project with every finding explained** — targets
the mart models.
- **Build & query it** — altimate-code detects whether dbt-core + dbt-duckdb
are installed, offers to reuse an existing dbt binary if you paste its
path, and walks you through the run. Auto-registers the DuckDB file as a
warehouse so `sql_execute` connects to the database dbt just built.

**If you `cd`'d into `~/altimate-sample-dbt/` from a different shell** — no
activation menu, no LLM in the loop. You can drive dbt yourself:

```bash
pip install dbt-duckdb
cd ~/altimate-sample-dbt # or wherever you materialized the sample
dbt seed # load the CSVs into DuckDB
dbt build # run models + tests
# Three PyPI packages that do different jobs:
# - dbt-core: provides the `dbt` binary (the entry point)
# - dbt-duckdb: adapter — teaches dbt-core how to talk to DuckDB
# - duckdb-cli: standalone `duckdb` binary the ad-hoc query line uses
# `pip install dbt-duckdb` pulls dbt-core as a dependency, so pip is fine.
# `pipx install dbt-duckdb` does NOT — pipx only exposes entry points from the
# named package, and dbt-duckdb has none. Use `--include-deps` if you go pipx.
pip install dbt-duckdb duckdb-cli # PEP 668 alternatives below
cd ~/altimate-sample-dbt # wherever you materialized the sample
dbt seed # load the CSVs into DuckDB
dbt build # run models + tests
duckdb target/jaffle.duckdb -c 'select * from customers'
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Once `dbt-duckdb` is on your `$PATH`, altimate-code detects it automatically
and the "run" workflows appear in `/help`.
If plain `pip` fails with `externally-managed-environment` (PEP 668, common
on modern macOS Homebrew and Debian/Ubuntu Python), use one of these instead:

```bash
pipx install --include-deps dbt-duckdb && pipx install duckdb-cli
# or
uv tool install dbt-core --with dbt-duckdb && uv tool install duckdb-cli
```

Once `dbt` is discoverable (venv, pipx, conda, uv, poetry, homebrew — the CLI
walks every common Python env manager), altimate-code's "Build & query"
option surfaces the same actions inside the chat.

## Bringing your own project

Expand Down
14 changes: 12 additions & 2 deletions packages/opencode/src/altimate/onboarding/materialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,17 @@ export function rejectUnsafeHome(home: string | undefined): string | undefined {
if (!home) return "HOME environment variable is not set"
if (home === "/" || home === "") return `HOME='${home}' is not a usable directory`
if (home === "/root" && process.getuid?.() !== 0) {
return "HOME=/root but this process is not running as root (likely `sudo npm install -g` — the sample would materialize into root's home and be invisible from your normal shell). Re-run without sudo, or pass an explicit `--target-parent`."
return (
"HOME=/root but this process is not running as root (likely `sudo npm install -g` — " +
"the sample would materialize into root's home and be invisible from your normal shell). " +
"Preferred fix: re-run altimate-code without sudo (install to a per-user prefix like " +
"`npm i -g --prefix ~/.local altimate-code`, or use nvm/asdf). If the CLI was already " +
"installed as root, drop `sudo` from the launch command — the wrapper reads HOME from " +
"your shell. If you must launch under sudo (locked-down environment only), pass your " +
"own HOME explicitly: `sudo -E HOME=\"$HOME\" altimate-code` — but note the sample and " +
"any files it materializes will be owned by root and need `sudo chown -R \"$USER\" " +
"~/altimate-sample-dbt` before you can modify them from your normal shell."
)
}
// Canonicalize BOTH sides of the tmp comparison before matching —
// otherwise a caller who passes `/private/tmp/foo` on macOS bypasses
Expand All @@ -186,7 +196,7 @@ export function rejectUnsafeHome(home: string | undefined): string | undefined {
for (const ref of refs) {
if (!ref) continue
if (candidate === ref || candidate.startsWith(ref + path.sep)) {
return `HOME='${home}' resolves to '${candidate}' which is under an ephemeral system tmp path (${ref}) — the sample would be hard to find or swept by tmp-cleanup jobs. Pass an explicit --target-parent to override.`
return `HOME='${home}' resolves to '${candidate}' which is under an ephemeral system tmp path (${ref}) — the sample would be hard to find or swept by tmp-cleanup jobs. Set HOME to a persistent user directory before launching (e.g. \`HOME=/Users/you altimate-code\`).`
}
}
return undefined
Expand Down
Loading
Loading