Merge origin/main into fix-uv-version-mismatch

This commit is contained in:
Luca Martial 2026-05-19 00:45:43 +02:00
commit 8b2b4fa0a7
17 changed files with 1145 additions and 828 deletions

View file

@ -0,0 +1,4 @@
import { source } from "@/lib/source";
import { createFromSource } from "fumadocs-core/search/server";
export const { GET } = createFromSource(source);

View file

@ -69,7 +69,11 @@
--color-fd-muted-foreground: #7a8d96;
}
html, body {
/* Keep html overflow at the default `visible` so body's overflow
propagates to the viewport (per CSS Overflow spec). That lets
`react-remove-scroll-bar` lock viewport scroll via body alone while
leaving the sticky sidebar placeholder anchored to the viewport. */
body {
overflow-x: clip;
}
@ -161,6 +165,17 @@ pre {
line-height: 1.7 !important;
}
/* Disable monospace ligatures so `--flag` keeps a visible space and double
dashes don't fuse into an em-dash glyph. */
code,
pre,
pre code,
.ktx-code,
.ktx-code code {
font-variant-ligatures: none !important;
font-feature-settings: "liga" 0, "calt" 0 !important;
}
.dark pre {
background: transparent !important;
}
@ -216,57 +231,10 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
margin: 0;
}
/* ── Mode A: Terminal ─────────────────────── */
.ktx-code-terminal {
background: #0c1417;
border: 1px solid rgba(255, 255, 255, 0.08);
color: #c8c3bc;
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.1),
0 12px 32px -16px rgba(0, 0, 0, 0.3);
}
.ktx-code-terminal:hover {
border-color: rgba(34, 211, 238, 0.2);
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.1),
0 14px 32px -12px rgba(34, 211, 238, 0.18);
}
.ktx-code-terminal-head {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent);
}
.ktx-tl-dot {
width: 11px;
height: 11px;
border-radius: 999px;
flex-shrink: 0;
}
.ktx-code-terminal-label {
margin-left: 8px;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.02em;
color: rgba(255, 255, 255, 0.4);
}
.ktx-code-body-terminal {
background: transparent !important;
color: #c8c3bc !important;
}
/* ── Mode D: Output preview (wizard prompts, status output) ── */
.ktx-code-output {
background: var(--color-fd-muted);
border: 1px solid var(--color-fd-border);
border-left: 3px solid color-mix(in oklch, var(--color-fd-primary) 50%, var(--color-fd-border));
position: relative;
box-shadow: 0 1px 2px rgba(27, 27, 24, 0.02);
}
@ -274,17 +242,14 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
.dark .ktx-code-output {
background: #111a1e;
border-color: rgba(255, 255, 255, 0.05);
border-left-color: rgba(34, 211, 238, 0.25);
}
.ktx-code-output:hover {
border-color: color-mix(in oklch, var(--color-fd-primary) 25%, var(--color-fd-border));
border-left-color: var(--color-fd-primary);
}
.dark .ktx-code-output:hover {
border-color: rgba(255, 255, 255, 0.08);
border-left-color: rgba(34, 211, 238, 0.45);
}
.ktx-code-output-label {
@ -304,8 +269,8 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
.ktx-code-output-copy {
position: absolute !important;
top: 6px !important;
right: 6px !important;
top: 7px !important;
right: 8px !important;
opacity: 0;
transform: translateY(-4px);
transition: opacity 0.2s var(--ktx-ease), transform 0.2s var(--ktx-ease);
@ -445,30 +410,10 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
border-color: rgba(34, 211, 238, 0.2);
}
.ktx-code-minimal-lang {
position: absolute;
top: 8px;
left: 14px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-fd-muted-foreground);
font-family: var(--font-display), var(--font-sans), sans-serif;
opacity: 0;
transition: opacity 0.2s var(--ktx-ease);
pointer-events: none;
z-index: 1;
}
.ktx-code-minimal:hover .ktx-code-minimal-lang {
opacity: 0.5;
}
.ktx-code-minimal-copy {
position: absolute !important;
top: 6px !important;
right: 6px !important;
top: 7px !important;
right: 8px !important;
opacity: 0;
transform: translateY(-4px);
transition: opacity 0.2s var(--ktx-ease), transform 0.2s var(--ktx-ease);
@ -778,8 +723,8 @@ body::after {
mix-blend-mode: overlay;
}
/* Make sure content stays above background */
body > * {
/* Make sure page content stays above the decorative background. */
.ktx-site-shell {
position: relative;
z-index: 2;
}
@ -1058,8 +1003,7 @@ body > * {
.pill-badge .pill-dot { animation: none; }
.card-lift { transition: none; }
.ktx-code,
.ktx-code-minimal-copy,
.ktx-code-minimal-lang {
.ktx-code-minimal-copy {
transition: none;
}
#nd-sidebar div[data-state]:not([class]) > button[data-state] svg {

View file

@ -41,7 +41,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
suppressHydrationWarning
>
<body>
<RootProvider>{children}</RootProvider>
<RootProvider search={{ options: { api: "/ktx/api/search" } }}>
<div className="ktx-site-shell">{children}</div>
</RootProvider>
</body>
</html>
);

View file

@ -13,7 +13,7 @@ type Props = ComponentPropsWithoutRef<"pre"> & {
"data-language"?: string;
};
const TERMINAL_LANGS = new Set(["bash", "sh", "shell", "zsh"]);
const OUTPUT_LANGS = new Set(["text", "plain", "plaintext", "console", "output"]);
const WIZARD_GLYPHS = /^\s*[◆◇◯◐○●]/;
function extractText(node: ReactNode): string {
@ -27,6 +27,33 @@ function extractText(node: ReactNode): string {
return "";
}
function findLanguageInNode(node: ReactNode): string | null {
if (!isValidElement(node)) return null;
const props = (node as ReactElement<{
className?: string;
"data-language"?: string;
children?: ReactNode;
}>).props;
const dataLang = props["data-language"];
if (typeof dataLang === "string" && dataLang) return dataLang;
const className = typeof props.className === "string" ? props.className : "";
const m = className.match(/language-([\w-]+)/);
if (m) return m[1];
const children = props.children;
if (Array.isArray(children)) {
for (const child of children) {
const found = findLanguageInNode(child);
if (found) return found;
}
} else if (children) {
return findLanguageInNode(children);
}
return null;
}
function detectLanguage(props: Props, children: ReactNode): string | null {
const dataLang = props["data-language"];
if (typeof dataLang === "string" && dataLang) return dataLang;
@ -35,14 +62,7 @@ function detectLanguage(props: Props, children: ReactNode): string | null {
const m = className.match(/language-([\w-]+)/);
if (m) return m[1];
if (isValidElement(children)) {
const childProps = (children as ReactElement<{ className?: string }>).props;
const childClass = typeof childProps.className === "string" ? childProps.className : "";
const cm = childClass.match(/language-([\w-]+)/);
if (cm) return cm[1];
}
return null;
return findLanguageInNode(children);
}
export function CodeBlock(props: Props) {
@ -50,32 +70,11 @@ export function CodeBlock(props: Props) {
const language = detectLanguage(props, children);
const codeText = extractText(children);
const isTerminal = language !== null && TERMINAL_LANGS.has(language);
const isOutput = !isTerminal && WIZARD_GLYPHS.test(codeText);
const hasTitle = typeof title === "string" && title.length > 0;
// Mode A - Terminal (commands the user types)
if (isTerminal) {
return (
<div className="not-prose ktx-code ktx-code-terminal group">
<div className="ktx-code-terminal-head">
<span className="ktx-tl-dot" style={{ background: "#ff5f57" }} />
<span className="ktx-tl-dot" style={{ background: "#febc2e" }} />
<span className="ktx-tl-dot" style={{ background: "#28c840" }} />
<span className="ktx-code-terminal-label">
{hasTitle ? title : "zsh"}
</span>
<CopyButton
text={codeText}
className="ml-auto text-white/80"
/>
</div>
<pre {...rest} className="ktx-code-body ktx-code-body-terminal">
{children}
</pre>
</div>
);
}
const isOutput =
!hasTitle &&
(WIZARD_GLYPHS.test(codeText) ||
(language !== null && OUTPUT_LANGS.has(language)));
// Mode D - Output preview (wizard prompts, terminal output)
if (isOutput) {
@ -110,7 +109,6 @@ export function CodeBlock(props: Props) {
// Mode C - Minimal default
return (
<div className="not-prose ktx-code ktx-code-minimal group relative">
{language && <span className="ktx-code-minimal-lang">{language}</span>}
<CopyButton text={codeText} className="ktx-code-minimal-copy" />
<pre {...rest} className="ktx-code-body ktx-code-body-minimal">
{children}

View file

@ -25,12 +25,12 @@ export function CopyButton({ text, className = "" }: Props) {
type="button"
onClick={onClick}
aria-label={copied ? "Copied" : "Copy code"}
className={`inline-flex items-center justify-center w-7 h-7 rounded-md transition-all hover:bg-white/5 ${className}`}
className={`inline-flex items-center justify-center w-9 h-9 rounded-md transition-all hover:bg-fd-muted ${className}`}
>
{copied ? (
<svg
width="14"
height="14"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
@ -44,8 +44,8 @@ export function CopyButton({ text, className = "" }: Props) {
</svg>
) : (
<svg
width="13"
height="13"
width="17"
height="17"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"

View file

@ -19,7 +19,7 @@ export function DocsPageActions({ mdxSource }: Props) {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch {
// Clipboard denied fail silently
// Clipboard denied - fail silently
}
};

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ directory. Use it from any directory to generate editor or agent schema files.
| Flag | Description | Default |
|------|-------------|---------|
| `--output <file>` | Write the schema to a file instead of stdout | |
| `--output <file>` | Write the schema to a file instead of stdout | - |
## `dev runtime` Subcommands

View file

@ -33,7 +33,7 @@ connections when you use `--all`.
| `--plain` | Print plain text output | `true` |
| `--json` | Print JSON output | `false` |
| `--yes` | Install required managed runtime features without prompting | `false` |
| `--no-input` | Disable interactive terminal input | |
| `--no-input` | Disable interactive terminal input | - |
`--fast` and `--deep` are mutually exclusive. Depth flags apply only to
database connections. Query-history flags apply only to database connections
@ -60,7 +60,7 @@ read one item from stdin.
| Flag | Description | Default |
|------|-------------|---------|
| `--text <content>` | Text content to ingest; repeat for a batch | `[]` |
| `--connection-id <connectionId>` | Optional KTX connection id for semantic-layer capture | |
| `--connection-id <connectionId>` | Optional KTX connection id for semantic-layer capture | - |
| `--user-id <id>` | Memory user id for capture attribution | `local-cli` |
| `--json` | Print JSON output | `false` |
| `--fail-fast` | Stop after the first failed text item | `false` |

View file

@ -1,6 +1,6 @@
---
title: Introduction
description: What KTX is, how it works, and where to start.
description: KTX is an open-source, self-improving context layer for data agents.
---
import { ProductMechanics } from "@/components/product-mechanics";
@ -23,46 +23,67 @@ import { ProductMechanics } from "@/components/product-mechanics";
Make analytics context usable by agents
</h1>
<p className="mt-4 max-w-2xl text-lg text-fd-muted-foreground" style={{ lineHeight: '1.7' }}>
{'KTX turns warehouse metadata, semantic definitions, BI usage, and team knowledge into local files and runtime tools that database agents can trust.'}
{'KTX is an open-source context layer for database agents. It turns warehouse metadata, BI models, query history, docs, and approved metric definitions into reviewable files agents can search and execute.'}
</p>
</div>
</div>
## Why KTX
## Why KTX helps
- Schemas show columns, not business rules.
- Agents need trusted metrics, joins, filters, caveats, and provenance.
- KTX captures that context before agents write SQL, docs, or semantic edits.
KTX gives agents a shared context workspace before they write SQL, answer a
question, or update analytics definitions.
## What KTX creates
- **Context as code.** KTX writes wiki pages and semantic-layer definitions as
git-based files you can review, diff, and merge.
- **Self-improving ingest.** KTX reads warehouses, BI tools, modeling code,
query history, and notes, then reconciles new evidence with accepted context.
- **Executable semantics.** Agents can use approved measures, joins, filters,
dimensions, and segments instead of rebuilding canonical SQL from scratch.
- **Agent-native access.** CLI and MCP tools let agents search context, compile
semantic queries, run read-only SQL, and propose updates.
| Path | What it gives agents |
|------|----------------------|
| `semantic-layer/` | Measures, dimensions, joins, grain, filters, segments |
| `wiki/` | Business definitions, caveats, policies, analyst notes |
| `raw-sources/` | Extracted metadata, scan output, relationship evidence |
| `.ktx/` | Local indexes, embeddings, setup state, runtime data |
KTX complements existing semantic layers by pairing metric definitions with the
surrounding business knowledge, caveats, provenance, and review workflow agents
need for data work.
## How KTX works
KTX has two connected sides: it builds and maintains the context layer, then
serves that context to agents at runtime.
| Side | What KTX does |
|------|---------------|
| **Ingest and auto-maintain knowledge** | Reads your data stack and company knowledge, reconciles new evidence with accepted context, and keeps changes to `semantic-layer/` plus `wiki/` as version-controlled diffs automatically. |
| **Serve agents at runtime** | Helps agents find the right wiki pages and semantic-layer entities, then compile or execute semantic queries through CLI and MCP tools. |
<ProductMechanics />
## Use it for
- **Generate SQL** from approved measures, dimensions, joins, and filters
- **Explain provenance** with wiki context and warehouse evidence
- **Repair context** through reviewable YAML and Markdown diffs
- **Work alongside** dbt, LookML, MetricFlow, Looker, Metabase, and warehouses
Use KTX when agents need more than raw database access. Agents can search wiki
context, find semantic-layer entities, compile trusted semantic queries, run
read-only SQL, and use the same tools through MCP.
Databases: SQLite, PostgreSQL, Snowflake, BigQuery, ClickHouse, MySQL, SQL
Server.
- Generate SQL from approved metrics, joins, filters, and dimensions.
- Explain metric provenance with wiki context and source evidence.
- Repair context through reviewable YAML and Markdown diffs.
- Work alongside dbt, MetricFlow, LookML, Looker, Metabase, Notion, and
supported databases.
## Start here
Choose the route that matches what you want to do next. The quickstart is the
best first step for users; contributor setup lives in the community docs.
<Cards>
<Card title="Quickstart" href="/docs/getting-started/quickstart">
Set up KTX and build your first context in under 10 minutes.
Install KTX, run setup, build context, and connect an agent.
</Card>
<Card title="Guides" href="/docs/guides/building-context">
Hands-on workflows for scanning, ingesting, writing, and serving.
<Card title="The Context Layer" href="/docs/concepts/the-context-layer">
Understand why agents need more than schema access and raw SQL.
</Card>
<Card title="Building Context" href="/docs/guides/building-context">
Refresh context from databases, BI tools, query history, and documents.
</Card>
<Card title="Writing Context" href="/docs/guides/writing-context">
Edit semantic-layer YAML and wiki Markdown safely.

View file

@ -1,135 +1,93 @@
---
title: Quickstart
description: Set up KTX, build local context, and connect your coding agent.
description: Install KTX, run setup, and connect your coding agent.
---
This guide gets a local analytics project ready for KTX. You will install the
CLI, run the setup wizard, connect a database, build context, and install agent
rules that teach your coding assistant which KTX commands to run.
This guide takes a local analytics project from empty to agent-ready. You'll
install the CLI, run one guided setup command, and hand the context to a
coding assistant.
If you are a coding assistant choosing a docs route, start with the
[Agent Quickstart](/docs/ai-resources/agent-quickstart). This page is the
human setup walkthrough.
If you're a coding assistant choosing a docs route, start with the
[Agent Quickstart](/docs/ai-resources/agent-quickstart) instead.
## What setup does
`ktx setup` is the main project workflow. It can create or resume `ktx.yaml`,
configure model and embedding providers, add database connections, add optional
context sources, build the first context artifacts, and install agent
integration.
When you run bare `ktx` in an interactive terminal outside a KTX project, the
CLI opens the same setup experience. Inside an existing project, `ktx setup`
resumes incomplete work or opens a menu for changing setup, connecting an
agent, checking status, or exploring a demo project.
<div
className="not-prose my-8 rounded-xl border border-l-4 p-5 sm:p-6"
style={{
borderColor: 'color-mix(in oklch, #ff8a4d 35%, transparent)',
borderLeftColor: '#ff8a4d',
background: 'color-mix(in oklch, #ff8a4d 8%, transparent)',
}}
>
<div
className="text-xs font-semibold uppercase tracking-wider"
style={{ color: '#ff8a4d' }}
>
No warehouse handy?
</div>
<div className="mt-2 text-base leading-relaxed text-fd-foreground">
Try KTX against a real data stack - Postgres, dbt, Metabase, and Notion
pre-loaded with the Orbit demo corpus. The page lists demo credentials
you can paste straight into `ktx setup`.
</div>
<a
href="https://kaelio.com/start"
className="mt-4 inline-flex items-center gap-1 text-base font-semibold no-underline hover:underline"
style={{
color: '#ff8a4d',
textDecorationColor: '#ff8a4d',
}}
>
Get demo credentials at kaelio.com/start →
</a>
</div>
## Install the CLI
Install the published `@kaelio/ktx` package:
Install the published package globally:
```bash
npm install -g @kaelio/ktx
```
Then run setup from the analytics project directory:
KTX is open source. If you'd like to hack on it or run from a local checkout,
the source lives at [github.com/kaelio/ktx](https://github.com/kaelio/ktx) -
see [Contributing](/docs/community/contributing) to get set up.
## Run setup
From your project directory, run:
```bash
ktx setup
```
The local checkout workflow is only for KTX contributors. See
[Contributing](/docs/community/contributing) for that path.
The wizard walks you through everything KTX needs in one pass:
## Step 1: Choose the project
1. **Project** - creates or resumes `ktx.yaml` in the current directory.
2. **LLM** - picks a Claude backend. The default uses your local Claude Code
session, so no API key is required. You can also use an Anthropic API key
or Vertex AI.
3. **Embeddings** - picks an embeddings backend. Choose OpenAI for hosted
embeddings or `sentence-transformers` to run locally without an API key.
4. **Database** - adds at least one primary connection. Supported drivers:
SQLite, PostgreSQL, MySQL, ClickHouse, SQL Server, BigQuery, and Snowflake.
5. **Context sources** - optionally adds dbt, MetricFlow, LookML, Looker,
Metabase, or Notion. You can skip and add them later.
6. **Build** - runs the first ingest so semantic-layer sources and wiki pages
are ready for agents.
7. **Agent integration** - installs project-local rules for Claude Code,
Codex, Cursor, OpenCode, or universal `.agents`.
In an interactive terminal, setup can create a new KTX project or resume the
nearest existing project. The main project file is `ktx.yaml`.
For scripted setup, pass the project directory explicitly:
```bash
ktx setup --project-dir ./analytics
```
If setup exits early, rerun `ktx setup` in the same directory. KTX keeps local
setup progress under `.ktx/setup/` and resumes from the remaining work.
## Step 2: Configure the LLM
KTX uses a Claude model for ingest agents that turn schemas, SQL, BI metadata,
and documents into semantic-layer sources and wiki context.
Setup supports three LLM provider paths:
| Provider | Use when | Credential model |
|----------|----------|------------------|
| Claude subscription (Pro/Max) | You want KTX to use your local Claude Code session | Claude Code local authentication |
| Anthropic API key | You have an Anthropic API key | `ANTHROPIC_API_KEY` or a local `file:` secret |
| Google Vertex AI for Anthropic Claude | Your organization runs Claude through Google Cloud | Application Default Credentials plus Vertex project and location |
For Anthropic API, setup can read the key from the environment or save a pasted
key to `.ktx/secrets/anthropic-api-key`. `ktx.yaml` stores an `env:` or `file:`
reference, not the raw key.
For Vertex AI, setup uses Google Application Default Credentials. It can read
your active `gcloud` project, list visible projects, or accept explicit
`--vertex-project` and `--vertex-location` values.
To use your local Claude Code session instead of an API key, set:
```yaml
llm:
provider:
backend: claude-code
models:
default: sonnet
triage: haiku
candidateExtraction: sonnet
curator: sonnet
reconcile: sonnet
repair: sonnet
```
`claude-code` uses the Claude Code authentication already configured on your
machine. It doesn't use `ANTHROPIC_API_KEY`, Vertex credentials, AI Gateway
tokens, or Bedrock credentials. In non-interactive setup, pass
`--llm-model opus`, `--llm-model sonnet`, `--llm-model haiku`, or a full Claude
model ID to select the Claude Code model.
Setup checks the selected model before saving. Anthropic API setup fetches live
Claude model choices when possible and falls back to bundled defaults if model
discovery is unavailable.
## Step 3: Configure embeddings
KTX uses embeddings for semantic search over semantic-layer sources, wiki
context, schema metadata, and relationship evidence.
| Backend | Default model | Notes |
|---------|---------------|-------|
| OpenAI | `text-embedding-3-small` | Recommended for hosted embeddings. Requires an OpenAI API key. |
| Local sentence-transformers | `all-MiniLM-L6-v2` | Runs through the KTX-managed Python runtime. No hosted embedding key is required. |
OpenAI setup reads `OPENAI_API_KEY` or saves a local secret file. Local
sentence-transformers setup can install and start the managed runtime during
setup. To prepare that runtime before setup, run:
If you choose local `sentence-transformers` embeddings, KTX uses the managed
Python runtime. To prepare it before setup, run:
```bash
ktx dev runtime install --feature local-embeddings --yes
ktx dev runtime start --feature local-embeddings
```
## Step 4: Add a database
KTX needs at least one primary database connection before it can build database
context. The wizard supports SQLite, PostgreSQL, MySQL, ClickHouse, SQL Server,
BigQuery, and Snowflake.
You can usually enter connection fields interactively or provide a URL. Secret
URLs can be stored as local files under `.ktx/secrets/` or referenced with
`env:NAME` in `ktx.yaml`.
After saving a connection, setup tests it and builds fast schema context:
During the database step, setup tests the saved connection and builds initial
schema context:
```text
Testing warehouse
@ -137,114 +95,24 @@ Testing warehouse
Building schema context for warehouse
Running fast database ingest
Database ready
warehouse - PostgreSQL - schema context complete
```
PostgreSQL, BigQuery, and Snowflake can also enable query-history ingest. Query
history helps KTX learn common query patterns, joins, service-account filters,
and warehouse-specific usage. BigQuery and Snowflake support a lookback window;
Postgres reads the current `pg_stat_statements` aggregate data instead.
If setup exits early, rerun `ktx setup` in the same directory. KTX keeps
progress under `.ktx/setup/` and resumes from the remaining work.
## Step 5: Add context sources
> **Note:** Running bare `ktx` in an interactive terminal outside a KTX
> project opens the same wizard. Inside a project, it opens a menu for
> resuming setup, connecting an agent, checking status, or exploring a
> pre-built demo project.
Context sources are optional, but they make the first context layer much richer.
Setup can add:
## Verify
| Source | Typical input | What KTX learns |
|--------|---------------|-----------------|
| dbt | Local project or Git repo | Models, columns, tests, descriptions, tags |
| MetricFlow | Local project or Git repo | Semantic models, metrics, dimensions, entities |
| LookML | Local files or Git repo | Views, explores, dimensions, measures, joins |
| Looker | API URL and credentials | Explores, looks, dashboards, model metadata |
| Metabase | API URL and key | Questions, dashboards, BI database mappings |
| Notion | Integration token and crawl settings | Business docs and knowledge pages |
Setup maps BI and source metadata back to your primary warehouse connection so
generated context points at the right tables.
You can skip this step and add sources later by rerunning `ktx setup`.
## Step 6: Build context
The context build turns configured databases and sources into local artifacts
agents can read. It runs database ingest first, then source ingest and memory
updates.
Fast database ingest records deterministic schema grounding. Deep ingest adds
AI-enriched descriptions, embeddings, relationship evidence, and query-history
context when configured.
When the build finishes, setup verifies that agent-ready context exists:
```text
KTX context is ready for agents.
Databases:
warehouse: deep context complete
Context sources:
dbt_main: memory update complete
Verification:
Agent context: ready
Semantic search: ready
```
If a foreground build is interrupted, rerun `ktx setup` or build the same target
with `ktx ingest <connectionId>`.
## Step 7: Install agent integration
The final setup step installs project-local rules for your coding assistant.
Supported targets are Claude Code, Codex, Cursor, OpenCode, and universal
`.agents`.
You can also run this step later:
```bash
ktx setup --agents --target codex
```
Claude Code and Codex also support global installs:
```bash
ktx setup --agents --target codex --global
```
Agent rules are CLI-based. They point agents at the KTX CLI path that created
the file, so agents do not need a separate `ktx` binary in `PATH`. If the CLI
path changes after reinstalling or moving a checkout, rerun `ktx setup --agents`.
## Generated files
KTX writes plain files so people and agents can inspect changes in git.
| Path | Purpose |
|------|---------|
| `ktx.yaml` | Project configuration for LLMs, embeddings, connections, context sources, and query-history settings |
| `.ktx/secrets/*` | Local secret files referenced from `ktx.yaml`; do not commit these |
| `.ktx/setup/*` | Local setup and context-build state |
| `.ktx/agents/install-manifest.json` | Manifest used to manage installed agent files |
| `semantic-layer/<connection-id>/*.yaml` | Semantic source definitions used for SQL generation |
| `wiki/global/*.md` | Shared business context and metric definitions |
| `wiki/user/<user-id>/*.md` | User-scoped notes and local context |
| `.claude/skills/ktx/SKILL.md` | Claude Code project skill |
| `.agents/skills/ktx/SKILL.md` | Codex or universal project skill |
| `.cursor/rules/ktx.mdc` | Cursor project rule |
| `.opencode/commands/ktx.md` | OpenCode project command |
## Verify setup
Run:
When setup finishes, check readiness:
```bash
ktx status
```
Example output:
```text
KTX project: /home/user/analytics
Project ready: yes
@ -256,15 +124,49 @@ KTX context built: yes
Agent integration ready: yes (codex:project)
```
Use JSON when an agent or script needs a structured readiness check:
For a structured check inside scripts, use `ktx status --json`.
```bash
ktx status --json
When setup builds deep context, its final context check looks like:
```text
KTX context is ready for agents.
Databases:
warehouse: deep context complete
Context sources:
dbt_main: memory update complete
```
## Scripted setup example
## Connect a coding agent
Use non-interactive setup when creating repeatable fixtures or automation:
The setup wizard installs project-local agent rules in the last step. To
install or change targets later:
```bash
ktx setup --agents
```
Claude Code and Codex also support global installs with `--global`. Agent
rules point at the KTX CLI path that created them, so agents don't need a
separate `ktx` binary on `PATH`. If the CLI path changes, rerun
`ktx setup --agents`.
## What setup writes
KTX writes plain files so people and agents can review changes in git.
| Path | Purpose |
|------|---------|
| `ktx.yaml` | Project configuration |
| `.ktx/secrets/*` | Local secret files referenced from `ktx.yaml` - do not commit |
| `semantic-layer/<connection-id>/*.yaml` | Semantic sources for SQL generation |
| `wiki/global/*.md` | Shared business context and metric definitions |
| `.claude/skills/ktx/`, `.agents/skills/ktx/`, `.cursor/rules/ktx.mdc`, `.opencode/commands/ktx.md` | Installed agent rules |
## Scripted setup
For repeatable fixtures and automation, skip prompts with flags:
```bash
ktx setup \
@ -287,23 +189,21 @@ ktx ingest warehouse --fast
See [ktx setup](/docs/cli-reference/ktx-setup) for the full automation flag
surface.
## Common errors
## Common issues
| Symptom | Likely cause | Recovery |
|---------|--------------|----------|
| `ktx: command not found` | The global package is not installed or your shell cannot find it | Reinstall `@kaelio/ktx` and open a new shell |
| Setup resumes the wrong project | `KTX_PROJECT_DIR` or the nearest `ktx.yaml` points somewhere else | Pass `--project-dir <path>` |
| Anthropic health check fails | API key, model id, or access is invalid | Fix `ANTHROPIC_API_KEY` or rerun setup with a different key or model |
| Vertex AI health check fails | Vertex API, Claude access, project, location, or IAM permissions are missing | Check the project, location, Application Default Credentials, and Vertex AI permissions |
| OpenAI embeddings fail | `OPENAI_API_KEY` is missing or invalid | Export the key or choose local sentence-transformers embeddings |
| Local embeddings fail | Managed Python runtime cannot install or start | Run `ktx dev runtime status`, then install the local embeddings runtime |
| Database test fails | Credentials, network access, database, warehouse, or schema is wrong | Test the same values with the database's native client, then rerun setup |
| Context is not built | Setup saved configuration but skipped or interrupted the build | Run `ktx setup` or `ktx ingest --all` |
| Agent integration is incomplete | Setup skipped the agents step or installed a different target | Run `ktx setup --agents --target <target>` |
| Symptom | Fix |
|---------|-----|
| `ktx: command not found` | Reinstall `@kaelio/ktx` and open a new shell |
| Setup resumes the wrong project | Pass `--project-dir <path>` |
| LLM or embeddings health check fails | Rerun setup and pick a different credential, model, or backend |
| Database test fails | Verify the same connection with the database's native client, then rerun setup |
| Agent integration is incomplete | Run `ktx setup --agents --target <target>` |
## Next steps
- Build and refresh context with [Building Context](/docs/guides/building-context).
- Edit semantic sources and wiki pages with [Writing Context](/docs/guides/writing-context).
- Refresh context with [Building Context](/docs/guides/building-context).
- Edit semantic sources and wiki pages with
[Writing Context](/docs/guides/writing-context).
- Connect more tools with [Agent Clients](/docs/integrations/agent-clients).
- Read [The Context Layer](/docs/concepts/the-context-layer) to understand the architecture.
- Read [The Context Layer](/docs/concepts/the-context-layer) to understand
the architecture.

View file

@ -10,6 +10,7 @@
"test": "node --test tests/*.test.mjs"
},
"dependencies": {
"@xyflow/react": "^12.10.2",
"fumadocs-core": "16.8.10",
"fumadocs-mdx": "15.0.4",
"fumadocs-ui": "16.8.10",
@ -18,11 +19,11 @@
"react-dom": "19.2.6"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^25.7.0",
"@types/react": "^19",
"@types/react-dom": "^19",
"typescript": "^6.0",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4"
"tailwindcss": "^4",
"typescript": "^6.0"
}
}

View file

@ -5,5 +5,13 @@ export const docs = defineDocs({
});
export default defineConfig({
mdxOptions: {},
mdxOptions: {
rehypeCodeOptions: {
addLanguageClass: true,
themes: {
light: "min-light",
dark: "github-dark",
},
},
},
});

View file

@ -111,3 +111,21 @@ test("/ktx/docs redirects to the docs introduction", async () => {
`${docsBasePath}/docs/getting-started/introduction`,
);
});
test("/ktx/api/search returns docs search results", async () => {
const response = await fetch(
`${docsSiteUrl}${docsBasePath}/api/search?query=setup`,
);
assert.equal(response.status, 200);
const results = await response.json();
assert.ok(Array.isArray(results), "search response should be an array");
assert.ok(
results.some(
(result) =>
typeof result.url === "string" && result.url.startsWith("/docs/"),
),
"search should return at least one docs result",
);
});

View file

@ -0,0 +1,53 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
const docsSiteDir = join(dirname(fileURLToPath(import.meta.url)), "..");
async function readDocsFile(path) {
return readFile(join(docsSiteDir, path), "utf8");
}
test("root provider uses the base-path-aware search API", async () => {
const layout = await readDocsFile("app/layout.tsx");
assert.match(layout, /search=\{\{/);
assert.match(layout, /api:\s*"\/ktx\/api\/search"/);
});
test("site background stacking does not target every body child", async () => {
const css = await readDocsFile("app/global.css");
assert.doesNotMatch(css, /body\s*>\s*\*\s*\{[^}]*z-index/s);
assert.match(css, /\.ktx-site-shell\s*\{[^}]*z-index:\s*2/s);
});
test("search lock relies on body overflow propagation, not html or sidebar overrides", async () => {
const css = await readDocsFile("app/global.css");
// Body still clips horizontal overflow defensively.
assert.match(css, /(^|\s)body\s*\{[^}]*overflow-x:\s*clip/s);
// html must keep its default `visible` overflow so body's lock
// (`overflow: hidden` from react-remove-scroll-bar) propagates to the
// viewport. Locking html directly breaks `position: sticky` on the
// sidebar placeholder.
assert.doesNotMatch(css, /(^|\s)html\s*,?\s*\{[^}]*overflow(-y|\s*:)\s*(hidden|clip)/s);
assert.doesNotMatch(
css,
/html:has\(body\[data-scroll-locked\]\)[^{]*\{[^}]*overflow:\s*(hidden|clip)/s,
);
// No site-specific overrides to body's data-scroll-locked overflow or
// to the sidebar placeholder when locked.
assert.doesNotMatch(
css,
/html\s+body\[data-scroll-locked\][^{]*\{[^}]*overflow:/s,
);
assert.doesNotMatch(
css,
/body\[data-scroll-locked\]\s+\[data-sidebar-placeholder\][^{]*\{[^}]*position:\s*fixed/s,
);
});

View file

@ -23,7 +23,7 @@ test("docs introduction frames the concept before showing product mechanics", as
const heroIndex = introduction.indexOf("Make analytics context");
const whyIndex = introduction.indexOf("## Why KTX");
const createsIndex = introduction.indexOf("## What KTX creates");
const worksIndex = introduction.indexOf("## How KTX works");
const mechanicsIndex = introduction.indexOf("<ProductMechanics />");
const useCaseIndex = introduction.indexOf("## Use it for");
const heroSource = introduction.slice(0, mechanicsIndex);
@ -34,12 +34,12 @@ test("docs introduction frames the concept before showing product mechanics", as
"problem framing should appear after the hero",
);
assert.ok(
createsIndex > whyIndex,
"artifact summary should appear after problem framing",
worksIndex > whyIndex,
"mechanics bridge should appear after problem framing",
);
assert.ok(
mechanicsIndex > createsIndex,
"mechanics component should appear after the artifact summary",
mechanicsIndex > worksIndex,
"mechanics component should appear after the mechanics bridge",
);
assert.ok(
mechanicsIndex < useCaseIndex,
@ -49,49 +49,47 @@ test("docs introduction frames the concept before showing product mechanics", as
assert.doesNotMatch(heroSource, /The Context Layer/);
assert.doesNotMatch(heroSource, /Building Context/);
assert.doesNotMatch(heroSource, /flex flex-wrap gap-3/);
assert.doesNotMatch(introduction, /raw-sources/);
assert.doesNotMatch(introduction, /\.ktx/);
});
test("product mechanics component covers source-specific context and SQL expansion", async () => {
test("product mechanics component explains ingestion outputs", async () => {
const component = await readDocsFile("components/product-mechanics.tsx");
for (const expectedText of [
"How KTX works",
"Build context from source evidence",
"Run agent requests through the model",
"Ingestion",
"Runtime",
"wiki/",
"semantic-layer/",
"raw-sources/",
".ktx/",
"sl_refs",
"Database structure",
"BI and usage evidence",
"Semantic modeling",
"Company documentation",
"Notion pages",
"Sources",
"KTX transforms evidence",
"KTX builds the model",
"Outputs KTX writes",
"Postgres",
"How ingestion works",
"Ingestion flow",
"From scattered source systems to agent-ready context",
"wiki/*.md",
"semantic-layer/*.yaml",
"Wiki",
"Semantic layer",
"Databases",
"BI tools",
"Modeling code",
"Docs and notes",
"Source adapters",
"Context builder",
"Reconciliation",
"Validation",
"PostgreSQL",
"Snowflake",
"BigQuery",
"and many others",
"Metabase",
"Looker",
"dbt",
"MetricFlow",
"LookML",
"extract evidence",
"reconcile entities",
"validate references",
"semantic query plan",
"dialect SQL",
"bounded rows",
"provenance",
"measure: orders.total_revenue",
"dimension: customers.segment",
"select",
"Notion",
"Any text",
"compile into SQL",
'"use client"',
"@xyflow/react",
"<ReactFlow",
"getSmoothStepPath",
"animateMotion",
"mechanics-particle",
"buildParticlePath",
]) {
assert.ok(
component.includes(expectedText),
@ -99,7 +97,27 @@ test("product mechanics component covers source-specific context and SQL expansi
);
}
assert.match(
component,
/nodesDraggable=\{false\}/,
"ReactFlow canvas should disable node dragging",
);
assert.match(
component,
/panOnDrag=\{false\}/,
"ReactFlow canvas should disable panning",
);
assert.match(
component,
/zoomOnScroll=\{false\}/,
"ReactFlow canvas should disable scroll zoom",
);
assert.doesNotMatch(component, /raw-sources/);
assert.doesNotMatch(component, /\.ktx/);
assert.doesNotMatch(component, /Product mechanics/);
assert.doesNotMatch(component, /How KTX works/);
assert.doesNotMatch(component, /Runtime/);
assert.doesNotMatch(component, /A semantic compiler for analytics agents/);
assert.doesNotMatch(component, /KTX does more than retrieve Markdown/);
assert.doesNotMatch(component, /Plain Markdown \+ RAG/);
@ -109,12 +127,9 @@ test("product mechanics component covers source-specific context and SQL expansi
assert.doesNotMatch(component, /KTX works in two moments/);
assert.doesNotMatch(component, /name: "Metabase and query history"/);
assert.doesNotMatch(component, /name: "dbt, MetricFlow, LookML"/);
assert.doesNotMatch(component, /query history/);
assert.doesNotMatch(component, /analyst notes/);
assert.doesNotMatch(component, /ClickHouse/);
assert.doesNotMatch(component, /MySQL/);
assert.doesNotMatch(component, /SQL Server/);
assert.doesNotMatch(component, /SQLite/);
assert.doesNotMatch(
component,
/\/ktx\/brand\/(?:postgresql|snowflake|bigquery|clickhouse|mysql|sqlserver|sqlite|metabase|dbt|looker|notion)\.svg/,

184
pnpm-lock.yaml generated
View file

@ -57,6 +57,9 @@ importers:
docs-site:
dependencies:
'@xyflow/react':
specifier: ^12.10.2
version: 12.10.2(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
fumadocs-core:
specifier: 16.8.10
version: 16.8.10(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@1.14.0(react@19.2.6))(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
@ -2748,6 +2751,24 @@ packages:
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
'@types/d3-color@3.1.3':
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
'@types/d3-drag@3.0.7':
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
'@types/d3-interpolate@3.0.4':
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
'@types/d3-selection@3.0.11':
resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
'@types/d3-transition@3.0.9':
resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
'@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
@ -2853,6 +2874,15 @@ packages:
'@vitest/utils@4.1.6':
resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==}
'@xyflow/react@12.10.2':
resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==}
peerDependencies:
react: '>=17'
react-dom: '>=17'
'@xyflow/system@0.0.76':
resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==}
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
@ -3139,6 +3169,9 @@ packages:
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
classcat@5.0.5:
resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==}
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
@ -3325,6 +3358,44 @@ packages:
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
d3-drag@3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
d3-transition@3.0.1:
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
engines: {node: '>=12'}
peerDependencies:
d3-selection: 2 - 3
d3-zoom@3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
@ -5937,6 +6008,11 @@ packages:
'@types/react':
optional: true
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@ -6158,6 +6234,21 @@ packages:
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
zustand@4.5.7:
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
engines: {node: '>=12.7.0'}
peerDependencies:
'@types/react': '>=16.8'
immer: '>=9.0.6'
react: '>=16.8'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@ -9061,6 +9152,27 @@ snapshots:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
'@types/d3-color@3.1.3': {}
'@types/d3-drag@3.0.7':
dependencies:
'@types/d3-selection': 3.0.11
'@types/d3-interpolate@3.0.4':
dependencies:
'@types/d3-color': 3.1.3
'@types/d3-selection@3.0.11': {}
'@types/d3-transition@3.0.9':
dependencies:
'@types/d3-selection': 3.0.11
'@types/d3-zoom@3.0.8':
dependencies:
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
@ -9191,6 +9303,29 @@ snapshots:
convert-source-map: 2.0.0
tinyrainbow: 3.1.0
'@xyflow/react@12.10.2(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@xyflow/system': 0.0.76
classcat: 5.0.5
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
zustand: 4.5.7(@types/react@19.2.14)(react@19.2.6)
transitivePeerDependencies:
- '@types/react'
- immer
'@xyflow/system@0.0.76':
dependencies:
'@types/d3-drag': 3.0.7
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
'@types/d3-transition': 3.0.9
'@types/d3-zoom': 3.0.8
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-zoom: 3.0.0
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
@ -9459,6 +9594,8 @@ snapshots:
dependencies:
clsx: 2.1.1
classcat@5.0.5: {}
clean-stack@2.2.0: {}
clean-stack@5.3.0:
@ -9631,6 +9768,42 @@ snapshots:
csstype@3.2.3: {}
d3-color@3.1.0: {}
d3-dispatch@3.0.1: {}
d3-drag@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
d3-ease@3.0.1: {}
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
d3-selection@3.0.0: {}
d3-timer@3.0.1: {}
d3-transition@3.0.1(d3-selection@3.0.0):
dependencies:
d3-color: 3.1.0
d3-dispatch: 3.0.1
d3-ease: 3.0.1
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-timer: 3.0.1
d3-zoom@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
data-uri-to-buffer@4.0.1: {}
debug@4.4.3:
@ -12730,6 +12903,10 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.14
use-sync-external-store@1.6.0(react@19.2.6):
dependencies:
react: 19.2.6
util-deprecate@1.0.2: {}
validate-npm-package-license@3.0.4:
@ -12907,4 +13084,11 @@ snapshots:
zod@4.4.3: {}
zustand@4.5.7(@types/react@19.2.14)(react@19.2.6):
dependencies:
use-sync-external-store: 1.6.0(react@19.2.6)
optionalDependencies:
'@types/react': 19.2.14
react: 19.2.6
zwitch@2.0.4: {}