Ketoy
Getting started

Get started with Ketoy

Three commands and Ketoy is wired into your Android project - plugin pinned, runtime bootstrapped, sample @KetoyComposable screen mounted, signed .ktx emitted into your APK. The ketoy CLI is an AI agent - bring your own model from Anthropic, OpenAI, Gemini, Mistral, Groq, xAI, OpenRouter, or local Ollama.

Alpha status - 0.3.4-alpha

The local-asset path is fully supported: ketoy init wires up your :app module, the compiler plugin emits a signed .ktx into your assets, and the runtime executes it at startup.

Cloud delivery - pushing a .ktx to a CDN so users get updates without going through the Play Store - is under active development and ships shortly. Track progress on the GitHub releases page.

Requirements

Ketoy 0.3.4-alpha
Node.js20+
Kotlin2.0.21 (auto-pinned)
Android Gradle Plugin8.x (auto-pinned)
JDK17
min SDK26
Jetpack ComposeBOM 2024.10.00 (auto-pinned)

Kotlin version is pinned during alpha

The Compose compiler plugin and the Ketoy compiler plugin are both pinned to 2.0.21 for the alpha. ketoy init pins your gradle/libs.versions.toml automatically to the matched Kotlin / AGP / Compose BOM combination. These pins are temporary - they go away once Ketoy ships its own embedded Kotlin compiler.

Step 01

Install the CLI

The npm package is ketoy-dev (the bare ketoy name is blocked by npm's similarity heuristic). The installed binary is still ketoy.

terminal
$ npm i -g ketoy-dev
$ ketoy version

Requires Node.js 20 or newer. Every command runs in the current working directory - there's nothing global to maintain beyond the API-key store at ~/.ketoy-cli/.

Step 02

Run ketoy init in your project

From the root of your Android project (the directory containing settings.gradle.kts and app/build.gradle.kts), run ketoy init. The CLI inspects your project, plans every change, and asks before applying.

terminal
$ ketoy init                        # interactive, asks before each edit
$ ketoy init -y                     # accept defaults, no prompts
$ ketoy init --dry-run              # show the plan, write nothing
$ ketoy init --hilt                 # opt into Hilt wiring
$ ketoy init --no-install-screen    # skip the MainActivity wrap

A typical interactive plan looks like this:

terminal · ketoy init
Detected: namespace=com.example.myapp, applicationId=com.example.myapp,
          minSdk=26, Hilt=no, Application=none, MainActivity=present

Plan (6 edits, 4 new files):
  edit  app/build.gradle.kts        + Ketoy plugin, dependencies, ketoy {} block
  edit  app/src/main/AndroidManifest.xml   + android:name=".MyApplication"
  edit  app/src/main/.../MainActivity.kt   wrap setContent { … } with KetoyScreen
  edit  gradle/libs.versions.toml          pin Kotlin 2.0.21, AGP 8.13.2, Compose BOM
  edit  .gitignore                         + **/keys/*-private.key
  new   app/src/main/.../MyApplication.kt
  new   app/src/main/.../ketoyscreens/HelloKetoyScreen.kt
  new   app/ketoy-capabilities.json
  new   .ketoy/state.json

Apply? [y/N]

What it does

  1. Detects your namespace, applicationId, minSdk, Hilt usage, existing Application class, and existing MainActivity.
  2. Pins versions in gradle/libs.versions.toml to the alpha-matched combination (Kotlin 2.0.21, AGP 8.13.2, Compose BOM 2024.10.00).
  3. Edits app/build.gradle.kts - inserts the dev.ketoy.compiler plugin, appends the BOM + runtime + annotations + capabilities + Material3 adapters dependencies, and appends the ketoy { } configuration block.
  4. Edits AndroidManifest.xml - sets android:name=".MyApplication" on <application> when no Application class exists.
  5. Creates MyApplication.kt with the full bootstrap - KetoyConfig, CapabilityRegistry, KetoyRuntime, generated-adapter / constructor registration, KetoyBundleLoader.
  6. Wraps MainActivity's setContent with CompositionLocalProvider(LocalKetoyRuntime, LocalKetoyBundleLoader) and a KetoyScreen entry point (use --no-install-screen to skip).
  7. Creates HelloKetoyScreen.kt - a sample @KetoyEntryPoint @KetoyComposable to confirm the pipeline works.
  8. Creates app/ketoy-capabilities.json (empty registry) and appends **/keys/*-private.key to .gitignore.
  9. Saves project state to .ketoy/state.json so future commands know your layout.

Hilt is opt-in via ketoy chat

The non-Hilt setup is the default - it works in any Compose project without a DI framework. If you opt in with --hilt, init aborts with a pointer to ketoy chat, where the agent handles Hilt wiring against your existing modules rather than guessing at a stock template.

Step 03

Pick a model provider - optional

This step is skippable. If you don't connect an AI provider, ketoy init, build, and analyze still work - they're plain tooling. You just won't have access to the AI features: ketoy chat, ketoy migrate, and ketoy doctor all require a configured provider.

Ketoy is powered by the Vercel AI SDK, so you bring your own key from any major provider. Source code never leaves your machine except as part of the prompts you explicitly send to the provider you chose. No telemetry, no remote logging.

terminal · authenticate a provider
$ ketoy auth anthropic         # Anthropic Claude
$ ketoy auth openai            # OpenAI
$ ketoy auth google            # Google Gemini
$ ketoy auth mistral           # Mistral
$ ketoy auth groq              # Groq
$ ketoy auth xai               # xAI
$ ketoy auth openrouter        # 200+ models via one key
$ ketoy auth ollama            # local - no API key, just a base URL

$ ketoy auth --list            # see what's configured (redacted)

Then pin a default model (or override per-command with --model):

terminal · pick a model
# Set a default model used by every command.
$ ketoy config set model anthropic:claude-sonnet-4-5
$ ketoy config set model openai:gpt-4o
$ ketoy config set model google:gemini-2.0-flash-exp
$ ketoy config set model openrouter:meta-llama/llama-3.1-405b-instruct

# Or override per-command.
$ ketoy chat --model anthropic:claude-sonnet-4-5

Where credentials live

API keys are stored at ~/.ketoy-cli/config.json with mode 0600. The CLI refuses to print them - ketoy config get apiKeys is intentionally blocked. Use ketoy auth --list for a redacted view.

Step 04

Build and run

After ketoy init finishes, the project is ready to build. The compiler plugin attaches to compileReleaseKotlin and emits a signed .ktx into app/src/main/assets/ketoy/main.ktx.

terminal
$ ketoy build

Launch the app - the KBC version of HelloKetoyScreen renders. Delete the .ktx, change the entry point to "DoesNotExist", or toggle airplane mode - the native fallback renders identically. That's the production contract.

Command tour

What else the CLI does

Once initialized, the agent stays useful for the day-to-day work - writing screens, migrating existing Compose UI, diagnosing build failures, and inspecting bundles.

terminal
$ ketoy chat
> add a settings screen with a dark-mode switch and version row

$ ketoy chat "convert app/src/main/.../HomeScreen.kt to a KetoyComposable"

$ ketoy migrate app/src/main/.../HomeScreen.kt app/src/main/.../ProfileScreen.kt

$ ketoy doctor                # runs default build, diagnoses failures
$ ketoy doctor :app:ketoyBundle

$ ketoy analyze app/src/main/assets/ketoy/main.ktx --manifest --strings

ketoy chat

Open an AI session in the project root. The agent has tools for reading files, proposing surgical edits, running Gradle tasks, and inspecting build output.

ketoy migrate

AI-driven per-file Compose → KBC migration. Point it at a Compose screen and it produces a @KetoyComposable equivalent, diff-and-confirmed.

ketoy doctor

Runs a Gradle task and diagnoses any compiler-plugin errors. Useful when :app:ketoyBundle fails and you want the agent to read the failure and propose a fix.

ketoy build

Equivalent to ./gradlew :app:ketoyBundle --rerun-tasks, with variant selection: --variant bundle|debug|release.

ketoy analyze

Inspect an existing .ktx bundle - view its manifest, extract the string table, or dump the full structure as JSON.

ketoy config

List, get, or set CLI configuration. Use it to change your default model or inspect (redacted) provider settings.

Trust model

Safety & privacy

The agent runs in your terminal, against your files, with your keys. The boring safety guarantees that make that workable:

  • No file is rewritten in full. Edits to build.gradle.kts, AndroidManifest.xml, MainActivity.kt, and your Application class are surgical - single-line additions or block appends at well-identified anchors. Existing dependencies, signing configs, themes, and ProGuard rules are never touched.
  • Diff-and-confirm on every edit touching a high-risk file. The agent's edit_file tool always shows a unified diff and prompts before applying.
  • Idempotent. Re-running ketoy init on an already-configured project is a no-op.
  • Allowlisted bash with hard refusal of compound commands for auto-approval. cat && rm -rf / looks like cat to a naive checker; the CLI refuses to auto-approve any command containing shell metacharacters (;, &&, ||, |, backticks, $(…), redirects).
  • No telemetry. No analytics, no usage reporting, no remote logging. The CLI talks directly to the provider you configured.
Roadmap

What ships next - cloud delivery

What you just built is the local-asset path: the .ktx ships inside the APK at compile time, and updates roll out through the Play Store like any other asset.

Cloud delivery - pushing a new .ktx to a CDN where connected devices fetch updates at the next process start (Ed25519-verified, ETag-cached, rolled back on minAppVersion mismatch) - uses KetoyBundleSource.Remote(url). The wire format, signature verification, ETag cache, and runtime activation order are already implemented. The hosted infrastructure (signing pipeline, signed-URL distribution, telemetry) is under active development and ships shortly.

Continue reading

Architecture overview

The five-layer stack and why KBC bytecode - not JSON - is the wire format.

Read the spec

Supported features

Every Material3 adapter, every capability, every Compose token Ketoy ships with.

See coverage

Custom adapters

Add your own components beyond Material3 - the KSP processor handles the rest.

Read the guide

Need help?

Join the Ketoy community on Discord, or open an issue on GitHub. We respond within a day.