@databricks/appkit-ui 0.36.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 (59) hide show
  1. package/CLAUDE.md +3 -2
  2. package/NOTICE.md +1 -0
  3. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  4. package/dist/cli/commands/plugin/create/resource-defaults.js +2 -1
  5. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -1
  6. package/dist/cli/commands/plugin/schema-resources.js +34 -51
  7. package/dist/cli/commands/plugin/schema-resources.js.map +1 -1
  8. package/dist/cli/commands/plugin/sync/sync.js +20 -6
  9. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  10. package/dist/cli/commands/plugin/validate/validate-manifest.js +71 -157
  11. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  12. package/dist/cli/commands/plugin/validate/validate.js +2 -2
  13. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  14. package/dist/cli/commands/setup.js +2 -2
  15. package/dist/cli/commands/setup.js.map +1 -1
  16. package/dist/react/charts/options.js +2 -1
  17. package/dist/react/charts/options.js.map +1 -1
  18. package/dist/react/hooks/index.d.ts +1 -0
  19. package/dist/react/hooks/index.js +1 -0
  20. package/dist/react/hooks/use-mobile.d.ts +5 -0
  21. package/dist/react/hooks/use-mobile.d.ts.map +1 -0
  22. package/dist/react/index.d.ts +2 -1
  23. package/dist/react/index.js +2 -1
  24. package/dist/react/table/data-table.js +2 -2
  25. package/dist/react/table/data-table.js.map +1 -1
  26. package/dist/react/ui/sidebar.js +1 -1
  27. package/dist/schemas/manifest.d.ts +1139 -0
  28. package/dist/schemas/manifest.d.ts.map +1 -0
  29. package/dist/schemas/manifest.js +524 -0
  30. package/dist/schemas/manifest.js.map +1 -0
  31. package/dist/shared/src/plugin.d.ts +1 -0
  32. package/dist/shared/src/plugin.d.ts.map +1 -1
  33. package/dist/shared/src/schemas/manifest.d.ts +1 -0
  34. package/docs/api/appkit/Enumeration.ResourceType.md +1 -1
  35. package/docs/api/appkit/Interface.PluginManifest.md +57 -3
  36. package/docs/api/appkit/Interface.ResourceEntry.md +6 -4
  37. package/docs/api/appkit/Interface.ResourceRequirement.md +7 -61
  38. package/docs/api/appkit/TypeAlias.ResourceFieldEntry.md +6 -0
  39. package/docs/api/appkit/Variable.agents.md +1 -1
  40. package/docs/api/appkit.md +12 -12
  41. package/docs/app-management.md +1 -1
  42. package/docs/development/ai-assisted-development.md +2 -2
  43. package/docs/development/local-development.md +1 -1
  44. package/docs/development/remote-bridge.md +1 -1
  45. package/docs/development/templates.md +118 -12
  46. package/docs/development.md +1 -1
  47. package/docs/plugins/agents.md +8 -4
  48. package/docs/plugins/custom-plugins.md +33 -23
  49. package/docs/plugins/lakebase.md +1 -1
  50. package/docs/plugins/manifest.md +293 -0
  51. package/docs.md +2 -2
  52. package/llms.txt +3 -2
  53. package/package.json +5 -4
  54. package/sbom.cdx.json +1 -1
  55. package/dist/schemas/plugin-manifest.generated.d.ts +0 -182
  56. package/dist/schemas/plugin-manifest.generated.d.ts.map +0 -1
  57. package/dist/schemas/plugin-manifest.schema.json +0 -489
  58. package/dist/schemas/template-plugins.schema.json +0 -113
  59. 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")
@@ -4,7 +4,7 @@ Beta plugin
4
4
 
5
5
  This plugin is currently **beta**. APIs may change between minor releases. Import from `@databricks/appkit/beta`. See [Plugin Stability Tiers](./docs/plugins/stability.md).
6
6
 
7
- The `agents` plugin turns a Databricks AppKit app into an AI-agent host. It loads agent definitions from markdown on disk (one folder per agent: `config/agents/<id>/agent.md`), from TypeScript (`createAgent(def)`), or both, and exposes them at `POST /invocations` alongside routes for chat, thread management, and cancellation.
7
+ The `agents` plugin turns a Databricks AppKit app into an AI-agent host. It loads agent definitions from markdown on disk (one folder per agent: `config/agents/<id>/agent.md`), from TypeScript (`createAgent(def)`), or both, and exposes them at `POST /invocations` and `POST /responses` (non-streaming, aliases) alongside `POST /chat` (streaming) and routes for thread management, cancellation, and HITL approval.
8
8
 
9
9
  This page covers the full lifecycle. For the hand-written primitives (`tool()`, `mcpServer()`), see [tools](./docs/plugins/server.md).
10
10
 
@@ -30,7 +30,7 @@ await createApp({
30
30
 
31
31
  ```
32
32
 
33
- That alone gives you a live HTTP server with `POST /invocations` wired to a markdown-driven agent.
33
+ That alone gives you a live HTTP server with `POST /invocations` (and its alias `POST /responses`) wired to a markdown-driven agent. Use `POST /chat` instead when you want the streaming, HITL-capable surface.
34
34
 
35
35
  ## Level 1: drop a markdown agent package[​](#level-1-drop-a-markdown-agent-package "Direct link to Level 1: drop a markdown agent package")
36
36
 
@@ -66,7 +66,11 @@ On startup the plugin:
66
66
 
67
67
  The agent starts with **no tools**. Tools are opt-in — declare them in frontmatter (Level 2 below) or opt into auto-inherit explicitly with `agents({ autoInheritTools: { file: true } })`. See "Auto-inherit posture" further down for what that costs and why it's off by default.
68
68
 
69
- Requests land at `POST /invocations` with an OpenAI Responses-compatible body. Every tool call runs through `asUser(req)` so SQL executes as the requesting user, file access respects Unity Catalog ACLs, and telemetry spans are created automatically.
69
+ Requests land at `POST /invocations` (or its alias `POST /responses`) with an OpenAI Responses-compatible body. These endpoints run the agent to completion and return a single JSON response — no SSE. Streaming clients should use `POST /chat`. Every tool call runs through `asUser(req)` so SQL executes as the requesting user, file access respects Unity Catalog ACLs, and telemetry spans are created automatically.
70
+
71
+ No HITL on `/invocations` and `/responses`
72
+
73
+ The non-streaming invoke surface has no way to surface a mid-call approval prompt back to the caller. When `approval.requireForDestructive` is enabled (default) and the resolved agent has any tool annotated with a mutating effect (`effect: "write" | "update" | "destructive"`, or the legacy `destructive: true`), `POST /invocations` and `POST /responses` reject the request with HTTP 400 before the adapter runs. Move HITL-capable agents to `POST /chat`, or disable approval via `agents({ approval: { requireForDestructive: false } })` for autonomous back-office agents.
70
74
 
71
75
  ## Level 2: scope tools in frontmatter[​](#level-2-scope-tools-in-frontmatter "Direct link to Level 2: scope tools in frontmatter")
72
76
 
@@ -386,7 +390,7 @@ The route enforces that the decider is the stream owner: an approve from a diffe
386
390
 
387
391
  The plugin enforces a handful of caps to protect a single-instance deployment from runaway prompts, misbehaving clients, or prompt-injected delegation cycles. Some are static (enforced by the request schema) and some are configurable via `agents({ limits: { ... } })`.
388
392
 
389
- **Static caps** (applied at `POST /chat` and `POST /invocations` request parsing):
393
+ **Static caps** (applied at `POST /chat`, `POST /invocations`, and `POST /responses` request parsing):
390
394
 
391
395
  | Field | Cap | Why |
392
396
  | ------------------------------------ | ----------------- | ----------------------------------------------------------------------------- |
@@ -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