@databricks/appkit-ui 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CLAUDE.md +3 -2
  2. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  3. package/dist/cli/commands/plugin/create/resource-defaults.js +2 -1
  4. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -1
  5. package/dist/cli/commands/plugin/schema-resources.js +34 -51
  6. package/dist/cli/commands/plugin/schema-resources.js.map +1 -1
  7. package/dist/cli/commands/plugin/sync/sync.js +20 -6
  8. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  9. package/dist/cli/commands/plugin/validate/validate-manifest.js +71 -157
  10. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  11. package/dist/cli/commands/plugin/validate/validate.js +2 -2
  12. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  13. package/dist/cli/commands/setup.js +2 -2
  14. package/dist/cli/commands/setup.js.map +1 -1
  15. package/dist/schemas/manifest.d.ts +1139 -0
  16. package/dist/schemas/manifest.d.ts.map +1 -0
  17. package/dist/schemas/manifest.js +524 -0
  18. package/dist/schemas/manifest.js.map +1 -0
  19. package/dist/shared/src/plugin.d.ts +1 -0
  20. package/dist/shared/src/plugin.d.ts.map +1 -1
  21. package/dist/shared/src/schemas/manifest.d.ts +1 -0
  22. package/docs/api/appkit/Enumeration.ResourceType.md +1 -1
  23. package/docs/api/appkit/Interface.PluginManifest.md +57 -3
  24. package/docs/api/appkit/Interface.ResourceEntry.md +6 -4
  25. package/docs/api/appkit/Interface.ResourceRequirement.md +7 -61
  26. package/docs/api/appkit/TypeAlias.ResourceFieldEntry.md +6 -0
  27. package/docs/api/appkit.md +6 -6
  28. package/docs/app-management.md +1 -1
  29. package/docs/development/ai-assisted-development.md +2 -2
  30. package/docs/development/local-development.md +1 -1
  31. package/docs/development/remote-bridge.md +1 -1
  32. package/docs/development/templates.md +118 -12
  33. package/docs/development.md +1 -1
  34. package/docs/plugins/custom-plugins.md +33 -23
  35. package/docs/plugins/lakebase.md +1 -1
  36. package/docs/plugins/manifest.md +293 -0
  37. package/docs.md +2 -2
  38. package/llms.txt +3 -2
  39. package/package.json +5 -4
  40. package/sbom.cdx.json +1 -1
  41. package/dist/schemas/plugin-manifest.generated.d.ts +0 -182
  42. package/dist/schemas/plugin-manifest.generated.d.ts.map +0 -1
  43. package/dist/schemas/plugin-manifest.schema.json +0 -489
  44. package/dist/schemas/template-plugins.schema.json +0 -113
  45. package/docs/api/appkit/Interface.ResourceFieldEntry.md +0 -82
@@ -47,19 +47,39 @@ The plugin manifest drives the CLI's behavior during `databricks apps init`:
47
47
  * **`app.yaml` generation** — resource fields produce `env` + `valueFrom` entries
48
48
  * **`databricks.yml` generation** — resource fields produce bundle variables and app resource entries
49
49
 
50
+ The synced manifest is generated by `appkit plugin sync --write` from each plugin's `manifest.json` (see [Plugin manifest](./docs/plugins/manifest.md) for the authoring contract). The on-disk shape carries a `version` field that the CLI uses to negotiate features:
51
+
52
+ * `"1.0"` / `"1.1"` — earlier shapes; still readable.
53
+ * `"2.0"` — current shape. Adds `scaffolding` (required by the CLI when `version` is `"2.0"`; enforced at parse time, not via the published JSON Schema) and the `origin` field on every resource field entry. JSON Schema published at `https://databricks.github.io/appkit/schemas/template-plugins.schema.json`.
54
+
50
55
  ### Resource field properties[​](#resource-field-properties "Direct link to Resource field properties")
51
56
 
52
- Each resource field in the manifest can have these properties:
57
+ Each resource field in the synced manifest can have these properties:
58
+
59
+ | Property | Description |
60
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
61
+ | `env` | Environment variable name written to `.env` and `app.yaml` |
62
+ | `description` | Shown in the interactive prompt and bundle variable description |
63
+ | `localOnly` | Only written to `.env` for local dev; excluded from `app.yaml` and bundle variables |
64
+ | `bundleIgnore` | Excluded from `databricks.yml` variables (but still in `.env`) |
65
+ | `value` | Default value used when no user input is provided |
66
+ | `resolve` | Auto-populated by CLI from API calls instead of prompting (see below) |
67
+ | `examples` | Example values shown in field descriptions |
68
+ | `discovery` | How the CLI lists candidate values for the field (see [Plugin manifest — Resource discovery](./docs/plugins/manifest.md#resource-discovery)). |
69
+ | `origin` | **v2.0** computed field. How the value is determined — see below. |
70
+
71
+ ### `origin` (v2.0)[​](#origin-v20 "Direct link to origin-v20")
53
72
 
54
- | Property | Description |
55
- | -------------- | ----------------------------------------------------------------------------------- |
56
- | `env` | Environment variable name written to `.env` and `app.yaml` |
57
- | `description` | Shown in the interactive prompt and bundle variable description |
58
- | `localOnly` | Only written to `.env` for local dev; excluded from `app.yaml` and bundle variables |
59
- | `bundleIgnore` | Excluded from `databricks.yml` variables (but still in `.env`) |
60
- | `value` | Default value used when no user input is provided |
61
- | `resolve` | Auto-populated by CLI from API calls instead of prompting (see below) |
62
- | `examples` | Example values shown in field descriptions |
73
+ `origin` is computed at sync time from the field's other properties — plugin authors do not write it. It tells scaffolding agents how each value reaches the running app:
74
+
75
+ | Origin | Trigger | Meaning |
76
+ | ------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------ |
77
+ | `"platform"` | `localOnly: true` | Auto-injected by Databricks Apps at deploy time. Generated for local `.env` only; absent from `app.yaml` and bundle variables. |
78
+ | `"static"` | `value` set | Hardcoded literal. CLI does not prompt. |
79
+ | `"cli"` | `resolve` set | Resolved by the CLI from API calls (e.g. `postgres:host`). |
80
+ | `"user"` | none of the above | User must provide the value at init time. |
81
+
82
+ Precedence is in the order above (`localOnly` wins over `value`, which wins over `resolve`). The transform overwrites any hand-edited `origin` on the next sync — drift between the on-disk value and the field's actual shape is not possible by construction.
63
83
 
64
84
  ### Resolvers[​](#resolvers "Direct link to Resolvers")
65
85
 
@@ -87,7 +107,93 @@ Example field definition:
87
107
 
88
108
  ```
89
109
 
110
+ After sync, the field carries `"origin": "platform"` (because `localOnly` takes precedence over `resolve` for local-only fields injected at deploy time).
111
+
112
+ ### `scaffolding.rules` propagation[​](#scaffoldingrules-propagation "Direct link to scaffoldingrules-propagation")
113
+
114
+ Each plugin's `scaffolding.rules` block (see [Plugin manifest — Scaffolding rules](./docs/plugins/manifest.md#scaffolding-rules)) is propagated unchanged into its entry in `appkit.plugins.json`. The CLI hands the merged plugin-level rules — alongside the top-level template `scaffolding.rules` — to scaffolding agents that drive `databricks apps init`.
115
+
116
+ Merge model:
117
+
118
+ 1. Gather rules from every selected plugin and from every plugin with `requiredByTemplate: true`.
119
+ 2. Apply the template-level `scaffolding.rules` on top.
120
+ 3. Plugin-level rules **override** skill-baked or template-level defaults at the same directive site.
121
+ 4. A plugin `must` that conflicts with a template `never` (or vice versa) stops the init flow — see the validation rules below.
122
+
123
+ ### `scaffolding` descriptor (v2.0)[​](#scaffolding-descriptor-v20 "Direct link to scaffolding-descriptor-v20")
124
+
125
+ The `scaffolding` block at the top level of `appkit.plugins.json` describes the scaffolding command, its flags, and the cross-cutting rules every scaffolding agent must respect. It is required by the CLI when `version` is `"2.0"`. The requirement is enforced at parse time — the published JSON Schema marks only `version` and `plugins` as top-level required fields because it cannot express conditional requirements.
126
+
127
+ ```json
128
+ {
129
+ "scaffolding": {
130
+ "command": "databricks apps init",
131
+ "flags": {
132
+ "--name": {
133
+ "description": "Project name — sets {{.projectName}} in package.json, databricks.yml, and .env. Required for non-interactive scaffolding.",
134
+ "required": true,
135
+ "pattern": "^[a-z][a-z0-9-]*$"
136
+ },
137
+ "--features": {
138
+ "description": "Plugins to enable (comma-separated, no spaces; must match keys in this manifest's plugins map)",
139
+ "required": false,
140
+ "pattern": "^[a-zA-Z0-9_-]+(,[a-zA-Z0-9_-]+)*$"
141
+ },
142
+ "--profile": {
143
+ "description": "Databricks CLI profile to use for authentication (global flag)",
144
+ "required": false
145
+ }
146
+ },
147
+ "rules": {
148
+ "must": [
149
+ "Keep all secrets and credentials only in app.yaml, databricks.yml, and/or .env"
150
+ ],
151
+ "should": [
152
+ "ask user when in doubt of resource to use for plugin"
153
+ ],
154
+ "never": [
155
+ "guess resources when multiple or no options are available",
156
+ "embed secrets in files that will go to the client-bundle"
157
+ ]
158
+ }
159
+ }
160
+ }
161
+
162
+ ```
163
+
164
+ | Field | Description |
165
+ | -------------- | ----------------------------------------------------------------------- |
166
+ | `command` | Scaffolding command the agent should invoke. |
167
+ | `flags` | Map of flag name to `{ description, required?, pattern?, default? }`. |
168
+ | `rules.must` | Actions the scaffolding agent must always perform. |
169
+ | `rules.should` | Recommended actions — applied unless overridden by a plugin-level rule. |
170
+ | `rules.never` | Actions the scaffolding agent must never perform. |
171
+
172
+ The example above shows three flags. The canonical flag set shipped with every synced template manifest is:
173
+
174
+ | Flag | Required | Description |
175
+ | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
176
+ | `--name` | yes | Project name — sets `{{.projectName}}` in `package.json`, `databricks.yml`, and `.env`. |
177
+ | `--template` | no | Template path (local directory or GitHub URL). |
178
+ | `--version` | no | AppKit version to use; defaults to auto-detected. |
179
+ | `--features` | no | Plugins to enable (comma-separated, no spaces; must match keys in the `plugins` map). |
180
+ | `--set` | no | Set resource values (format: `plugin.resourceKey.field=value`, repeatable). |
181
+ | `--output-dir` | no | Directory to write the project to. |
182
+ | `--description` | no | App description. |
183
+ | `--run` | no | Run the app after creation (`none`, `dev`, `dev-remote`). |
184
+ | `--auto-approve` | no | Skip prompts for optional resources. Not recommended for agent-driven init — conflicts with the "ask user when in doubt" rule. |
185
+ | `--profile` | no | Databricks CLI profile to use for authentication (global flag). |
186
+
187
+ Each rule item is capped at **120 characters** by the schema. Long prose fails validation — split into discrete actionable directives.
188
+
189
+ The descriptor is canonical: AppKit owns the values and ships them with every synced template manifest. Authors of consuming agents (LLM-driven scaffolders, custom CLI runners) should treat the rule lists as enforcement contracts, not suggestions.
190
+
191
+ #### Substitutability gate (template rules)[​](#substitutability-gate-template-rules "Direct link to Substitutability gate (template rules)")
192
+
193
+ The template-level rules survive the same substitutability gate that governs plugin-level rules: each entry must describe a **cross-cutting agent decision** the schema cannot already encode. Rules like "modify only files inside the template directory" or "list volumes after prompting for catalog/schema" are absent on purpose — the first is unreachable once you read the manifest as the source of truth, and the second is now encoded structurally as `parents: ["catalog", "schema"]` on the `volume` discovery kind (see [Plugin manifest — Transient prompts](./docs/plugins/manifest.md#transient-prompts-parents)).
194
+
90
195
  ## See also[​](#see-also "Direct link to See also")
91
196
 
92
- * [Plugin management](./docs/plugins/plugin-management.md) — `appkit plugin sync`, `appkit plugin create`
93
- * [Configuration](./docs/configuration.md) — environment variables
197
+ * [Plugin manifest](./docs/plugins/manifest.md) — the authoring side (`manifest.json`).
198
+ * [Plugin management](./docs/plugins/plugin-management.md) — `appkit plugin sync`, `appkit plugin create`.
199
+ * [Configuration](./docs/configuration.md) — environment variables.
@@ -5,7 +5,7 @@ AppKit provides multiple development workflows to suit different needs: local de
5
5
  ## Prerequisites[​](#prerequisites "Direct link to Prerequisites")
6
6
 
7
7
  * [Node.js](https://nodejs.org) v22+ environment with `npm`
8
- * Databricks CLI (v0.295.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
8
+ * Databricks CLI (v1.0.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
9
9
  * A new Databricks app with AppKit installed. See [Bootstrap a new Databricks app](./docs.md#quick-start-options) for more details.
10
10
 
11
11
  ## Development flows[​](#development-flows "Direct link to Development flows")
@@ -15,34 +15,42 @@ For a deeper understanding of the plugin structure, read on.
15
15
 
16
16
  ## Basic plugin example[​](#basic-plugin-example "Direct link to Basic plugin example")
17
17
 
18
- Extend the [`Plugin`](./docs/api/appkit/Class.Plugin.md) class and export with `toPlugin()`:
18
+ Author the manifest as JSON, import it, and attach it to a [`Plugin`](./docs/api/appkit/Class.Plugin.md) subclass via `static manifest`. Export with `toPlugin()`:
19
+
20
+ ```json
21
+ // my-plugin/manifest.json
22
+ {
23
+ "$schema": "https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json",
24
+ "name": "my-plugin",
25
+ "displayName": "My Plugin",
26
+ "description": "A custom plugin",
27
+ "resources": {
28
+ "required": [
29
+ {
30
+ "type": "secret",
31
+ "alias": "apiKey",
32
+ "resourceKey": "api-key",
33
+ "description": "API key for external service",
34
+ "permission": "READ",
35
+ "fields": {
36
+ "scope": { "env": "MY_SECRET_SCOPE", "description": "Secret scope" },
37
+ "key": { "env": "MY_API_KEY", "description": "Secret key name" }
38
+ }
39
+ }
40
+ ],
41
+ "optional": []
42
+ }
43
+ }
44
+
45
+ ```
19
46
 
20
47
  ```typescript
48
+ // my-plugin/index.ts
21
49
  import { Plugin, toPlugin, type PluginManifest } from "@databricks/appkit";
22
- import type express from "express";
50
+ import manifest from "./manifest.json";
23
51
 
24
52
  class MyPlugin extends Plugin {
25
- static manifest = {
26
- name: "myPlugin",
27
- displayName: "My Plugin",
28
- description: "A custom plugin",
29
- resources: {
30
- required: [
31
- {
32
- type: "secret",
33
- alias: "apiKey",
34
- resourceKey: "apiKey",
35
- description: "API key for external service",
36
- permission: "READ",
37
- fields: {
38
- scope: { env: "MY_SECRET_SCOPE", description: "Secret scope" },
39
- key: { env: "MY_API_KEY", description: "Secret key name" }
40
- }
41
- }
42
- ],
43
- optional: []
44
- }
45
- } satisfies PluginManifest<"myPlugin">;
53
+ static manifest = manifest as PluginManifest<"my-plugin">;
46
54
 
47
55
  async setup() {
48
56
  // Initialize your plugin
@@ -67,6 +75,8 @@ export const myPlugin = toPlugin(MyPlugin);
67
75
 
68
76
  ```
69
77
 
78
+ JSON is the canonical authoring surface — it is what `appkit plugin sync` reads when aggregating manifests for templates. For the full v2.0 manifest contract (resources, discovery descriptors, scaffolding rules), see [Plugin manifest](./docs/plugins/manifest.md).
79
+
70
80
  ## Config-dependent resources[​](#config-dependent-resources "Direct link to Config-dependent resources")
71
81
 
72
82
  The manifest defines resources as either `required` (always needed) or `optional` (may be needed). For resources that become required based on plugin configuration, implement a static `getResourceRequirements(config)` method:
@@ -17,7 +17,7 @@ The easiest way to get started with the Lakebase plugin is to use the Databricks
17
17
  ### Prerequisites[​](#prerequisites "Direct link to Prerequisites")
18
18
 
19
19
  * [Node.js](https://nodejs.org) v22+ environment with `npm`
20
- * Databricks CLI (v0.295.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
20
+ * Databricks CLI (v1.0.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
21
21
  * A new Databricks app with AppKit installed. See [Bootstrap a new Databricks app](./docs.md#quick-start-options) for more details.
22
22
 
23
23
  ### Steps[​](#steps "Direct link to Steps")
@@ -0,0 +1,293 @@
1
+ # Plugin manifest
2
+
3
+ Every plugin ships a `manifest.json` next to its source code. The manifest declares plugin metadata, the Databricks resources the plugin needs, and any structured rules a scaffolding agent must honor when running `databricks apps init`. It is consumed at three stages:
4
+
5
+ * **Authoring** — `import manifest from "./manifest.json"` and attach it to the `Plugin` subclass via `static manifest`.
6
+ * **Sync** — `appkit plugin sync --write` aggregates manifests from installed packages and local plugins into `appkit.plugins.json`.
7
+ * **Init** — `databricks apps init` reads `appkit.plugins.json` to drive plugin selection, resource prompts, and `.env` / `databricks.yml` / `app.yaml` generation.
8
+
9
+ This page documents the **v2.0** manifest contract. JSON Schema is published at `https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json`; reference it via `$schema` for editor validation.
10
+
11
+ ## Recommended pattern[​](#recommended-pattern "Direct link to Recommended pattern")
12
+
13
+ Author the manifest as JSON, import it into the plugin module, and assert the type:
14
+
15
+ ```typescript
16
+ // packages/my-plugin/src/index.ts
17
+ import { Plugin, toPlugin } from "@databricks/appkit";
18
+ import type { PluginManifest } from "@databricks/appkit";
19
+ import manifest from "./manifest.json";
20
+
21
+ class MyPlugin extends Plugin {
22
+ static manifest = manifest as PluginManifest<"my-plugin">;
23
+ // ...
24
+ }
25
+
26
+ export const myPlugin = toPlugin(MyPlugin);
27
+
28
+ ```
29
+
30
+ ```json
31
+ // packages/my-plugin/src/manifest.json
32
+ {
33
+ "$schema": "https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json",
34
+ "name": "my-plugin",
35
+ "displayName": "My Plugin",
36
+ "description": "A custom plugin",
37
+ "resources": {
38
+ "required": [],
39
+ "optional": []
40
+ }
41
+ }
42
+
43
+ ```
44
+
45
+ JSON is the canonical authoring surface — it is what `appkit plugin sync` reads. JS manifests (`manifest.js` / `manifest.cjs`) are ignored by default and require `--allow-js-manifest` to opt in (executes plugin code; trust required). For end-to-end CLI behavior, see [Plugin management](./docs/plugins/plugin-management.md).
46
+
47
+ ## Required fields[​](#required-fields "Direct link to Required fields")
48
+
49
+ | Field | Type | Notes |
50
+ | -------------------- | ----------------------- | --------------------------------------------------------------------- |
51
+ | `name` | `string` | Plugin identifier. Lowercase, starts with a letter, `[a-z0-9-]` only. |
52
+ | `displayName` | `string` | Shown in UI and CLI prompts. |
53
+ | `description` | `string` | Brief summary. |
54
+ | `resources.required` | `ResourceRequirement[]` | Resources the plugin cannot run without. |
55
+ | `resources.optional` | `ResourceRequirement[]` | Resources that enhance behavior but are not mandatory. |
56
+
57
+ ## Resources[​](#resources "Direct link to Resources")
58
+
59
+ A resource requirement declares one Databricks resource the plugin depends on. The shape is keyed by `type`; each type fixes its valid `permission` values (validated by the schema as a discriminated union):
60
+
61
+ | `type` | Permissions |
62
+ | --------------------- | ----------------------------------------------- |
63
+ | `secret` | `READ`, `WRITE`, `MANAGE` |
64
+ | `job` | `CAN_VIEW`, `CAN_MANAGE_RUN`, `CAN_MANAGE` |
65
+ | `sql_warehouse` | `CAN_USE`, `CAN_MANAGE` |
66
+ | `serving_endpoint` | `CAN_VIEW`, `CAN_QUERY`, `CAN_MANAGE` |
67
+ | `volume` | `READ_VOLUME`, `WRITE_VOLUME` |
68
+ | `vector_search_index` | `SELECT` |
69
+ | `uc_function` | `EXECUTE` |
70
+ | `uc_connection` | `USE_CONNECTION` |
71
+ | `database` | `CAN_CONNECT_AND_CREATE` |
72
+ | `postgres` | `CAN_CONNECT_AND_CREATE` |
73
+ | `genie_space` | `CAN_VIEW`, `CAN_RUN`, `CAN_EDIT`, `CAN_MANAGE` |
74
+ | `experiment` | `CAN_READ`, `CAN_EDIT`, `CAN_MANAGE` |
75
+ | `app` | `CAN_USE` |
76
+
77
+ Every requirement has:
78
+
79
+ * `alias` — human-readable label used in UI / CLI output.
80
+ * `resourceKey` — stable machine key (`[a-z][a-z0-9-]*`). Used for deduplication, env naming, and references in `app.yaml`. **Identity is keyed on `resourceKey`, not `alias`.**
81
+ * `description` — explains *why* this resource is needed; surfaces in interactive prompts.
82
+ * `fields` — map of field name → field entry (see below). At least one entry when present.
83
+ * `permission` — must match the type's allowed enum.
84
+
85
+ Single-value resource types (e.g. `sql_warehouse`) typically declare one field (`id`). Multi-value types (e.g. `secret`, `database`) declare several (`scope` + `key`, `instance_name` + `database_name`).
86
+
87
+ ### Field entry[​](#field-entry "Direct link to Field entry")
88
+
89
+ ```json
90
+ {
91
+ "id": {
92
+ "env": "DATABRICKS_WAREHOUSE_ID",
93
+ "description": "SQL Warehouse ID",
94
+ "examples": ["1234abcd5678efgh"],
95
+ "discovery": { "type": "kind", "resourceKind": "warehouse" }
96
+ }
97
+ }
98
+
99
+ ```
100
+
101
+ | Property | Description |
102
+ | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
103
+ | `env` | Environment variable name written to `.env` and `app.yaml`. Must match `^[A-Z][A-Z0-9_]*$`. |
104
+ | `description` | Shown in interactive prompts and bundle variable descriptions. |
105
+ | `examples` | Sample values shown in field descriptions. |
106
+ | `localOnly` | When `true`, the field is generated for local `.env` only — the Databricks Apps platform auto-injects it at deploy time, so it is excluded from `app.yaml` and `databricks.yml`. |
107
+ | `bundleIgnore` | Excluded from `databricks.yml` variables (still written to `.env`). |
108
+ | `value` | Static default value. |
109
+ | `resolve` | CLI-side resolver name, formatted `<resource_type>:<field>` (e.g. `postgres:host`). The CLI populates the value from API calls during init. |
110
+ | `discovery` | Describes how the CLI lists candidate values — see below. |
111
+
112
+ ### Configuration-dependent resources[​](#configuration-dependent-resources "Direct link to Configuration-dependent resources")
113
+
114
+ The manifest distinguishes `required` from `optional` for static analysis. When a resource only becomes required based on the plugin's runtime config, list it under `optional` in the manifest and override at runtime via a static `getResourceRequirements(config)` method on the plugin class. See [Creating custom plugins](./docs/plugins/custom-plugins.md#config-dependent-resources).
115
+
116
+ ## Resource discovery[​](#resource-discovery "Direct link to Resource discovery")
117
+
118
+ Discovery describes how the CLI offers candidate values for a field during interactive init. There are two variants under `discovery`, discriminated by `type`:
119
+
120
+ ### `kind` variant (preferred)[​](#kind-variant-preferred "Direct link to kind-variant-preferred")
121
+
122
+ ```json
123
+ {
124
+ "discovery": {
125
+ "type": "kind",
126
+ "resourceKind": "warehouse"
127
+ }
128
+ }
129
+
130
+ ```
131
+
132
+ The `kind` variant references a well-known Databricks resource kind for which AppKit owns the listing command and response shape. This is the preferred form for first-party Databricks resources — plugin authors declare *what* to list, and AppKit owns *how* to list it.
133
+
134
+ Supported `resourceKind` values:
135
+
136
+ | `resourceKind` | Listed via |
137
+ | ------------------- | --------------------------------------------- |
138
+ | `warehouse` | `databricks warehouses list` |
139
+ | `genie_space` | `databricks genie list-spaces` |
140
+ | `volume` | `databricks volumes list {catalog} {schema}` |
141
+ | `postgres_project` | `databricks postgres list-projects` |
142
+ | `postgres_branch` | `databricks postgres list-branches {project}` |
143
+ | `postgres_database` | `databricks postgres list-databases {branch}` |
144
+
145
+ Supported options on the `kind` variant:
146
+
147
+ | Property | Description |
148
+ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
149
+ | `select` | Field name in the parsed CLI response used as the selected value (e.g. `"id"`, `"name"`, `"full_name"`). Defaults to the kind's natural identifier. |
150
+ | `display` | Field name shown to the user during selection. Defaults to `select`. |
151
+ | `dependsOn` | Name of a sibling field within the same resource that must resolve first (see [Field dependencies](#field-dependencies)). |
152
+ | `shortcut` | Single-value fast-path command that returns exactly one value, skipping interactive selection. |
153
+
154
+ ### `cli` variant (escape hatch)[​](#cli-variant-escape-hatch "Direct link to cli-variant-escape-hatch")
155
+
156
+ For resources outside the `kind` map, fall back to the `cli` variant:
157
+
158
+ ```json
159
+ {
160
+ "discovery": {
161
+ "type": "cli",
162
+ "cliCommand": "databricks custom-resource list --profile <PROFILE> --output json",
163
+ "selectField": ".id",
164
+ "displayField": ".name"
165
+ }
166
+ }
167
+
168
+ ```
169
+
170
+ | Property | Description |
171
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
172
+ | `cliCommand` | Full Databricks CLI command. **Must include the literal `<PROFILE>` placeholder** — the runner substitutes the user's CLI profile. Shell metacharacters (`;`, `\|`, `&`, `` ` ``, `$`, newlines) are rejected — executors pass arguments via argv, never `shell-exec` the string. |
173
+ | `selectField` | jq-style path to the field used as the selected value (e.g. `.id`, `.name`). |
174
+ | `displayField` | jq-style path to the field shown to the user. Defaults to `selectField`. |
175
+ | `dependsOn` | Sibling field that must resolve first. |
176
+ | `shortcut` | Single-value fast-path command. Same metacharacter restriction as `cliCommand`. |
177
+
178
+ The `cli` variant is intentionally minimal and may tighten in future versions. **Prefer the `kind` variant** for any resource AppKit knows about; it gives you a single source of truth for command + unwrap rules and guarantees forward-compat as AppKit refines the discovery contract.
179
+
180
+ ### Field dependencies[​](#field-dependencies "Direct link to Field dependencies")
181
+
182
+ When listing one resource depends on another (e.g. listing volumes requires a catalog and schema; listing Postgres branches requires a project), use `dependsOn` to declare ordering:
183
+
184
+ ```json
185
+ {
186
+ "fields": {
187
+ "project": {
188
+ "discovery": { "type": "kind", "resourceKind": "postgres_project", "select": "name" }
189
+ },
190
+ "branch": {
191
+ "discovery": {
192
+ "type": "kind",
193
+ "resourceKind": "postgres_branch",
194
+ "select": "name",
195
+ "dependsOn": "project"
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ ```
202
+
203
+ `dependsOn` references a sibling field name within the same resource. The CLI prompts in dependency order and substitutes the resolved value into the parent command (e.g. `{project}` in `databricks postgres list-branches {project}`).
204
+
205
+ The schema validates the dependency graph at parse time:
206
+
207
+ * Dangling references (`dependsOn` pointing at a non-existent sibling) are rejected.
208
+ * Cycles are rejected with the chain listed (`a → b → a`).
209
+
210
+ ### Transient prompts (`parents`)[​](#transient-prompts-parents "Direct link to transient-prompts-parents")
211
+
212
+ Some `kind`-variant commands need values that are **not** sibling fields on the resource — they are query inputs the runner collects once and discards. AppKit declares these on the `kind` itself via a `parents` array on `RESOURCE_KIND_COMMANDS`.
213
+
214
+ The only kind that uses `parents` today is `volume`:
215
+
216
+ ```text
217
+ volume → parents: ["catalog", "schema"]
218
+
219
+ ```
220
+
221
+ Before invoking `databricks volumes list {catalog} {schema} --profile <PROFILE> --output json`, the runner prompts the user for each `parents` entry as free text and substitutes the value into the matching `{name}` placeholder. Unlike `dependsOn`, the collected values are **not** persisted as resource fields — they exist only for the duration of the listing call.
222
+
223
+ Plugin authors do not declare `parents` in their manifest; it is part of the AppKit-owned `kind` contract and surfaces in the published JSON Schema alongside each kind's command template.
224
+
225
+ ## Scaffolding rules[​](#scaffolding-rules "Direct link to Scaffolding rules")
226
+
227
+ `scaffolding.rules` is the plugin-level handoff to scaffolding agents (LLM-driven runners, custom CLI workflows, the `databricks-apps` skill). It carries up to three short directive lists — `must`, `should`, `never` — that the agent honors when invoking `databricks apps init` with this plugin selected.
228
+
229
+ ```json
230
+ {
231
+ "scaffolding": {
232
+ "rules": {
233
+ "should": [
234
+ "After init, run any database migrations for your chosen ORM before first request",
235
+ "After init, verify Lakebase connectivity with 'psql $PGHOST -c \"select 1\"'"
236
+ ]
237
+ }
238
+ }
239
+ }
240
+
241
+ ```
242
+
243
+ | Bucket | Semantics |
244
+ | -------- | ----------------------------------------------------- |
245
+ | `must` | The agent must perform the action. |
246
+ | `should` | Recommended action — agent applies unless overridden. |
247
+ | `never` | The agent must not perform the action. |
248
+
249
+ ### Authoring contract[​](#authoring-contract "Direct link to Authoring contract")
250
+
251
+ * Each entry is a single short directive, **capped at 120 characters** by the schema. Long prose fails validation; split it into discrete actionable items.
252
+ * The schema enforces both **per-bucket dedup** (no two entries with the same text inside one of `must` / `should` / `never`) and **cross-bucket dedup** (one entry cannot belong to two buckets at once).
253
+ * Use the `Before init` / `After init` prefix convention when ordering matters so consumers can sequence directives consistently.
254
+
255
+ ### Substitutability gate[​](#substitutability-gate "Direct link to Substitutability gate")
256
+
257
+ A rule belongs in the manifest **only if it cannot be expressed as structured data** somewhere else — a resource permission, a `discovery` descriptor, a `dependsOn` chain, a `requiredByTemplate` flag, a config field, or the field's `env` / `value` / `resolve` slot.
258
+
259
+ Examples of what **does** survive the gate:
260
+
261
+ * `"After init, run any database migrations for your chosen ORM before first request"` — runtime sequencing, not derivable from any resource shape.
262
+ * `"After init, configure the 'spaces' map in plugin config with alias-to-Space-ID mappings"` — config-population guidance the schema cannot encode.
263
+
264
+ Examples of what does **not** survive (and should be modeled instead):
265
+
266
+ * "Plugin X requires `READ_VOLUME` on its volume" → already encoded in the resource's `permission` field.
267
+ * "The runner must list Postgres branches after a project is chosen" → already encoded via `dependsOn`.
268
+ * "Prompt the user for catalog and schema before listing volumes" → already encoded via `RESOURCE_KIND_COMMANDS.volume.parents`.
269
+
270
+ If you find yourself writing prose that the schema could capture, extend the schema instead.
271
+
272
+ The rules block is propagated unchanged from the plugin manifest into the synced template manifest. See [Templates — `scaffolding.rules` propagation](./docs/development/templates.md#scaffoldingrules-propagation) for how the CLI merges plugin-level rules with the template-level rules block.
273
+
274
+ ## Optional fields[​](#optional-fields "Direct link to Optional fields")
275
+
276
+ | Field | Description |
277
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
278
+ | `author` | Author name or organization. |
279
+ | `version` | Plugin version, semver format (`X.Y.Z` or `X.Y.Z-prerelease`). |
280
+ | `repository` | URL to the plugin source. |
281
+ | `keywords` | Discovery keywords. |
282
+ | `license` | SPDX identifier. |
283
+ | `onSetupMessage` | One-shot message displayed after init. Use for short hints; prefer `scaffolding.rules` for actionable directives an agent must enforce. |
284
+ | `hidden` | When `true`, the plugin is excluded from the synced template manifest. |
285
+ | `stability` | `"beta"` or `"ga"`. Beta plugins may break across minor releases — see [Plugin stability tiers](./docs/plugins/stability.md). |
286
+ | `config.schema` | JSON Schema for the plugin's runtime config (used by the type generator and for validation). |
287
+
288
+ ## See also[​](#see-also "Direct link to See also")
289
+
290
+ * [Creating custom plugins](./docs/plugins/custom-plugins.md) — building a plugin from scratch.
291
+ * [Plugin management](./docs/plugins/plugin-management.md) — `appkit plugin sync`, `create`, `validate`, `add-resource`.
292
+ * [Templates](./docs/development/templates.md) — how the synced template manifest drives `databricks apps init`.
293
+ * [`PluginManifest` API reference](./docs/api/appkit/Interface.PluginManifest.md) — TypeScript type.
package/docs.md CHANGED
@@ -19,7 +19,7 @@ AppKit simplifies building data applications on Databricks by providing:
19
19
  ## Prerequisites[​](#prerequisites "Direct link to Prerequisites")
20
20
 
21
21
  * [Node.js](https://nodejs.org) v22+ environment with `npm`
22
- * Databricks CLI (v0.295.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
22
+ * Databricks CLI (v1.0.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
23
23
 
24
24
  ## Quick start options[​](#quick-start-options "Direct link to Quick start options")
25
25
 
@@ -37,7 +37,7 @@ Databricks AppKit is designed to work with AI coding assistants through Agent Sk
37
37
  Install Agent Skills and configure it for use with your preferred AI assistant:
38
38
 
39
39
  ```bash
40
- databricks experimental aitools install
40
+ databricks aitools install
41
41
 
42
42
  ```
43
43
 
package/llms.txt CHANGED
@@ -52,6 +52,7 @@ npx @databricks/appkit docs <query>
52
52
  - [Genie plugin](./docs/plugins/genie.md): Integrates Databricks AI/BI Genie spaces into your AppKit application, enabling natural language data queries via a conversational interface.
53
53
  - [Jobs plugin](./docs/plugins/jobs.md): Trigger and monitor Databricks Lakeflow Jobs from your AppKit application.
54
54
  - [Lakebase plugin](./docs/plugins/lakebase.md): Provides a PostgreSQL connection pool for Databricks Lakebase Autoscaling with automatic OAuth token refresh.
55
+ - [Plugin manifest](./docs/plugins/manifest.md): Every plugin ships a manifest.json next to its source code. The manifest declares plugin metadata, the Databricks resources the plugin needs, and any structured rules a scaffolding agent must honor when running databricks apps init. It is consumed at three stages:
55
56
  - [Model Serving plugin](./docs/plugins/model-serving.md): Provides an authenticated proxy to Databricks Model Serving endpoints, with invoke and streaming support.
56
57
  - [Plugin management](./docs/plugins/plugin-management.md): AppKit includes a CLI for managing plugins. All commands are available under npx @databricks/appkit plugin.
57
58
  - [Server plugin](./docs/plugins/server.md): Provides HTTP server capabilities with development and production modes.
@@ -76,7 +77,7 @@ npx @databricks/appkit docs <query>
76
77
  - [Class: TunnelError](./docs/api/appkit/Class.TunnelError.md): Error thrown when remote tunnel operations fail.
77
78
  - [Class: ValidationError](./docs/api/appkit/Class.ValidationError.md): Error thrown when input validation fails.
78
79
  - [Enumeration: RequestedClaimsPermissionSet](./docs/api/appkit/Enumeration.RequestedClaimsPermissionSet.md): Permission set for Unity Catalog table access
79
- - [Enumeration: ResourceType](./docs/api/appkit/Enumeration.ResourceType.md): Resource types from schema $defs.resourceType.enum
80
+ - [Enumeration: ResourceType](./docs/api/appkit/Enumeration.ResourceType.md): Resource types from resourceTypeSchema.options
80
81
  - [Function: agentIdFromMarkdownPath()](./docs/api/appkit/Function.agentIdFromMarkdownPath.md): Derives the logical agent id from a markdown path. When the file is named
81
82
  - [Function: appKitServingTypesPlugin()](./docs/api/appkit/Function.appKitServingTypesPlugin.md): Vite plugin to generate TypeScript types for AppKit serving endpoints.
82
83
  - [Function: appKitTypesPlugin()](./docs/api/appkit/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
@@ -141,7 +142,6 @@ npx @databricks/appkit docs <query>
141
142
  - [Interface: RequestedClaims](./docs/api/appkit/Interface.RequestedClaims.md): Optional claims for fine-grained Unity Catalog table permissions
142
143
  - [Interface: RequestedResource](./docs/api/appkit/Interface.RequestedResource.md): Resource to request permissions for in Unity Catalog
143
144
  - [Interface: ResourceEntry](./docs/api/appkit/Interface.ResourceEntry.md): Internal representation of a resource in the registry.
144
- - [Interface: ResourceFieldEntry](./docs/api/appkit/Interface.ResourceFieldEntry.md): Defines a single field for a resource. Each field has its own environment variable and optional description. Single-value types use one key (e.g. id); multi-value types (database, secret) use multiple (e.g. instancename, databasename or scope, key).
145
145
  - [Interface: ResourceRequirement](./docs/api/appkit/Interface.ResourceRequirement.md): Declares a resource requirement for a plugin.
146
146
  - [Interface: RunAgentInput](./docs/api/appkit/Interface.RunAgentInput.md): Properties
147
147
  - [Interface: RunAgentResult](./docs/api/appkit/Interface.RunAgentResult.md): Properties
@@ -174,6 +174,7 @@ npx @databricks/appkit docs <query>
174
174
  - [Type Alias: PluginData<T, U, N>](./docs/api/appkit/TypeAlias.PluginData.md): Tuple of plugin class, config, and name. Created by toPlugin() and passed to createApp().
175
175
  - [Type Alias: Plugins](./docs/api/appkit/TypeAlias.Plugins.md): Plugin map passed to the function form of AgentDefinition.tools.
176
176
  - [Type Alias: ResolvedToolEntry](./docs/api/appkit/TypeAlias.ResolvedToolEntry.md): Internal tool-index entry after a tool record has been resolved to a dispatchable form.
177
+ - [Type Alias: ResourceFieldEntry](./docs/api/appkit/TypeAlias.ResourceFieldEntry.md)
177
178
  - [Type Alias: ResourcePermission](./docs/api/appkit/TypeAlias.ResourcePermission.md): Union of all possible permission levels across all resource types.
178
179
  - [Type Alias: ServingFactory](./docs/api/appkit/TypeAlias.ServingFactory.md): Factory function returned by AppKit.serving.
179
180
  - [Type Alias: ToolRegistry](./docs/api/appkit/TypeAlias.ToolRegistry.md)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@databricks/appkit-ui",
3
3
  "type": "module",
4
- "version": "0.37.0",
4
+ "version": "0.38.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -37,6 +37,7 @@
37
37
  "clean": "rm -rf dist tmp",
38
38
  "dist": "tsx ../../tools/dist-appkit.ts",
39
39
  "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp",
40
+ "tarball:prerelease": "rm -rf tmp && SHORTSHA=$(git rev-parse --short HEAD) && pnpm dist --prerelease $SHORTSHA && npm pack ./tmp --pack-destination ./tmp",
40
41
  "typecheck": "tsc --noEmit",
41
42
  "postinstall": "node scripts/postinstall.js"
42
43
  },
@@ -87,10 +88,10 @@
87
88
  "tailwind-merge": "3.4.0",
88
89
  "vaul": "1.1.2",
89
90
  "@ast-grep/napi": "0.37.0",
90
- "ajv": "8.17.1",
91
- "ajv-formats": "3.0.1",
91
+ "@standard-schema/spec": "1.1.0",
92
92
  "@clack/prompts": "1.0.1",
93
- "commander": "12.1.0"
93
+ "commander": "12.1.0",
94
+ "zod": "4.3.6"
94
95
  },
95
96
  "peerDependencies": {
96
97
  "react": "^18.0.0 || ^19.0.0",