Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
2 changes: 1 addition & 1 deletion .opencode/skills/dbt-analyze/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ If no manifest is available:
1. Run `lineage_check` on the changed SQL
2. Show column-level data flow
3. Note: downstream impact requires a manifest
4. Suggest: `altimate-dbt build-project` to generate one
4. Suggest: `altimate-dbt build` to generate one

## Common Mistakes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ altimate-dbt info # Project name, adapter, root
## Build & Run

```bash
altimate-dbt build --model <name> [--downstream] # compile + run + test
altimate-dbt build # full project build (compile + run + test)
Comment thread
suryaiyer95 marked this conversation as resolved.
altimate-dbt build --model <name> [--downstream] # build a single model
altimate-dbt run --model <name> [--downstream] # materialize only
altimate-dbt test --model <name> # run tests only
altimate-dbt build-project # full project build
```

## Compile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ altimate-dbt info # Project name, adapter, root
## Build & Run

```bash
altimate-dbt build --model <name> [--downstream] # compile + run + test
altimate-dbt build # full project build (compile + run + test)
altimate-dbt build --model <name> [--downstream] # build a single model
altimate-dbt run --model <name> [--downstream] # materialize only
altimate-dbt test --model <name> # run tests only
altimate-dbt build-project # full project build
```

## Compile
Expand Down
4 changes: 2 additions & 2 deletions .opencode/skills/dbt-docs/references/altimate-dbt-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ altimate-dbt info # Project name, adapter, root
## Build & Run

```bash
altimate-dbt build --model <name> [--downstream] # compile + run + test
altimate-dbt build # full project build (compile + run + test)
altimate-dbt build --model <name> [--downstream] # build a single model
altimate-dbt run --model <name> [--downstream] # materialize only
altimate-dbt test --model <name> # run tests only
altimate-dbt build-project # full project build
```

## Compile
Expand Down
4 changes: 2 additions & 2 deletions .opencode/skills/dbt-test/references/altimate-dbt-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ altimate-dbt info # Project name, adapter, root
## Build & Run

```bash
altimate-dbt build --model <name> [--downstream] # compile + run + test
altimate-dbt build # full project build (compile + run + test)
altimate-dbt build --model <name> [--downstream] # build a single model
altimate-dbt run --model <name> [--downstream] # materialize only
altimate-dbt test --model <name> # run tests only
altimate-dbt build-project # full project build
```

## Compile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ altimate-dbt info # Project name, adapter, root
## Build & Run

```bash
altimate-dbt build --model <name> [--downstream] # compile + run + test
altimate-dbt build # full project build (compile + run + test)
altimate-dbt build --model <name> [--downstream] # build a single model
altimate-dbt run --model <name> [--downstream] # materialize only
altimate-dbt test --model <name> # run tests only
altimate-dbt build-project # full project build
```

## Compile
Expand Down
2 changes: 1 addition & 1 deletion packages/dbt-tools/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DBTProjectIntegrationAdapter, CommandProcessResult } from "@altima

export async function build(adapter: DBTProjectIntegrationAdapter, args: string[]) {
const model = flag(args, "model")
if (!model) return { error: "Missing --model" }
if (!model) return project(adapter)
const downstream = args.includes("--downstream")
const result = await adapter.unsafeBuildModelImmediately({
plusOperatorLeft: "",
Expand Down
6 changes: 1 addition & 5 deletions packages/dbt-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ const USAGE = {
info: "Get project info (paths, targets, version)",
compile: "Compile a model (Jinja to SQL) --model <name>",
"compile-query": "Compile a raw query --query <sql> [--model <name>]",
build: "Build a model --model <name> [--downstream]",
build: "Build project, or a single model with --model <name> [--downstream]",
run: "Run a model --model <name> [--downstream]",
test: "Test a model --model <name>",
"build-project": "Build entire project",
execute: "Execute SQL --query <sql> [--model <name>] [--limit <n>]",
columns: "Get columns of model --model <name>",
"columns-source": "Get columns of source --source <name> --table <name>",
Expand Down Expand Up @@ -171,9 +170,6 @@ async function main() {
case "test":
result = await (await import("./commands/build")).test(adapter, rest)
break
case "build-project":
result = await (await import("./commands/build")).project(adapter)
break
case "execute":
result = await (await import("./commands/execute")).execute(adapter, rest)
break
Expand Down
57 changes: 57 additions & 0 deletions packages/dbt-tools/test/build.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, test, expect, mock } from "bun:test"
import { build } from "../src/commands/build"
import type { DBTProjectIntegrationAdapter } from "@altimateai/dbt-integration"

function makeAdapter(overrides: Partial<DBTProjectIntegrationAdapter> = {}): DBTProjectIntegrationAdapter {
return {
unsafeBuildModelImmediately: mock(() => Promise.resolve({ stdout: "model built", stderr: "" })),
unsafeBuildProjectImmediately: mock(() => Promise.resolve({ stdout: "project built", stderr: "" })),
unsafeRunModelImmediately: mock(() => Promise.resolve({ stdout: "", stderr: "" })),
unsafeRunModelTestImmediately: mock(() => Promise.resolve({ stdout: "", stderr: "" })),
dispose: mock(() => Promise.resolve()),
...overrides,
} as unknown as DBTProjectIntegrationAdapter
}

describe("build command", () => {
test("build without --model builds entire project", async () => {
const adapter = makeAdapter()
const result = await build(adapter, [])
expect(adapter.unsafeBuildProjectImmediately).toHaveBeenCalledTimes(1)
expect(adapter.unsafeBuildModelImmediately).not.toHaveBeenCalled()
expect(result).toEqual({ stdout: "project built" })
})

test("build --model <name> builds single model", async () => {
const adapter = makeAdapter()
const result = await build(adapter, ["--model", "orders"])
expect(adapter.unsafeBuildModelImmediately).toHaveBeenCalledTimes(1)
expect(adapter.unsafeBuildModelImmediately).toHaveBeenCalledWith({
plusOperatorLeft: "",
modelName: "orders",
plusOperatorRight: "",
})
expect(adapter.unsafeBuildProjectImmediately).not.toHaveBeenCalled()
expect(result).toEqual({ stdout: "model built" })
})

test("build --model <name> --downstream sets plusOperatorRight", async () => {
const adapter = makeAdapter()
await build(adapter, ["--model", "orders", "--downstream"])
expect(adapter.unsafeBuildModelImmediately).toHaveBeenCalledWith({
plusOperatorLeft: "",
modelName: "orders",
plusOperatorRight: "+",
})
})

test("build surfaces stderr as error", async () => {
const adapter = makeAdapter({
unsafeBuildProjectImmediately: mock(() =>
Promise.resolve({ stdout: "partial output", stderr: "compilation error", fullOutput: "" }),
),
})
const result = await build(adapter, [])
expect(result).toEqual({ error: "compilation error", stdout: "partial output" })
})
})
2 changes: 2 additions & 0 deletions packages/opencode/src/altimate/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ export namespace Telemetry {
skill_name: string
skill_source: "builtin" | "global" | "project"
duration_ms: number
has_followups: boolean
followup_count: number
}
// altimate_change start — first_launch event for new user counting (privacy-safe: only version + machine_id)
| {
Expand Down
183 changes: 183 additions & 0 deletions packages/opencode/src/skill/followups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// altimate_change start — skill follow-up suggestions for conversational engagement
export namespace SkillFollowups {
export interface Suggestion {
skill: string // skill name to suggest
label: string // short display label
description: string // why this is a good next step
condition?: string // optional: when this suggestion applies
}

// Map from skill name to follow-up suggestions
const FOLLOWUPS: Record<string, Suggestion[]> = {
"dbt-develop": [
{
skill: "dbt-test",
label: "Add tests",
description: "Write schema tests and unit tests for the model you just created to ensure data quality.",
},
{
skill: "dbt-docs",
label: "Document your model",
description: "Add descriptions to your model and columns in schema.yml for discoverability.",
},
{
skill: "dbt-analyze",
label: "Check downstream impact",
description: "Analyze the blast radius of your changes on downstream models before merging.",
},
{
skill: "sql-review",
label: "Review SQL quality",
description: "Run a quality gate on your SQL — lint for anti-patterns and grade readability.",
},
],
"dbt-troubleshoot": [
{
skill: "dbt-test",
label: "Add regression tests",
description: "Now that the bug is fixed, add tests to prevent it from recurring.",
},
{
skill: "dbt-analyze",
label: "Check downstream impact",
description: "Verify your fix didn't break downstream models.",
},
{
skill: "dbt-develop",
label: "Improve the model",
description: "Refactor or extend the model now that it's working correctly.",
},
],
"dbt-test": [
{
skill: "dbt-develop",
label: "Build more models",
description: "Continue building new models in your dbt project.",
},
{
skill: "dbt-docs",
label: "Document tested models",
description: "Add documentation to the models you just tested.",
},
],
"dbt-docs": [
{
skill: "dbt-test",
label: "Add tests",
description: "Add data quality tests for the models you just documented.",
},
{
skill: "dbt-analyze",
label: "Analyze lineage",
description: "Review column-level lineage to ensure documentation matches data flow.",
},
],
"dbt-analyze": [
{
skill: "dbt-test",
label: "Add tests for affected models",
description: "Add tests to downstream models that could be impacted by changes.",
},
{
skill: "dbt-develop",
label: "Make the changes",
description: "Proceed with implementing the changes now that you understand the impact.",
},
],
"sql-review": [
{
skill: "query-optimize",
label: "Optimize performance",
description: "Improve query performance based on the review findings.",
},
{
skill: "sql-translate",
label: "Translate to another dialect",
description: "Port this SQL to a different database dialect.",
},
],
"sql-translate": [
{
skill: "sql-review",
label: "Review translated SQL",
description: "Run a quality check on the translated SQL to catch dialect-specific issues.",
},
],
"query-optimize": [
{
skill: "sql-review",
label: "Review optimized query",
description: "Run a quality gate on the optimized SQL.",
},
{
skill: "cost-report",
label: "Check cost impact",
description: "Analyze how the optimization affects query costs.",
},
],
"cost-report": [
{
skill: "query-optimize",
label: "Optimize expensive queries",
description: "Optimize the most expensive queries identified in the report.",
},
],
"pii-audit": [
{
skill: "sql-review",
label: "Review SQL for PII exposure",
description: "Check specific queries for PII leakage.",
},
],
"lineage-diff": [
{
skill: "dbt-analyze",
label: "Full impact analysis",
description: "Run a comprehensive impact analysis on the changed models.",
},
{
skill: "dbt-test",
label: "Add tests for changed paths",
description: "Add tests covering the changed data flow paths.",
},
],
"schema-migration": [
{
skill: "dbt-develop",
label: "Update dbt models",
description: "Update your dbt models to reflect the schema changes.",
},
],
}

// A special warehouse nudge for users who haven't connected yet
const WAREHOUSE_NUDGE = "**Tip:** Connect a warehouse to validate against real data. Run `/discover` to auto-detect your connections."

export function get(skillName: string): readonly Suggestion[] {
return Object.freeze(FOLLOWUPS[skillName] ?? [])
}

export function format(skillName: string): string {
const suggestions = get(skillName)
if (suggestions.length === 0) return ""

const lines = [
"",
"---",
"",
"## What's Next?",
"",
"Now that this task is complete, here are suggested next steps:",
"",
...suggestions.map(
(s, i) => `${i + 1}. **${s.label}** — ${s.description} → Use \`/skill ${s.skill}\` or just ask me.`,
),
"",
WAREHOUSE_NUDGE,
"",
"*You can continue this conversation — just type your next request.*",
]
return lines.join("\n")
}
}
// altimate_change end
Loading
Loading