diff --git a/.release-it.json b/.release-it.json
index 76443ed4..980cc513 100644
--- a/.release-it.json
+++ b/.release-it.json
@@ -20,7 +20,7 @@
"npm": false,
"hooks": {
"after:bump": "tsx tools/sync-versions.ts ${version} && pnpm build:notice && git add NOTICE.md",
- "before:release": "pnpm build && pnpm --filter=@databricks/appkit dist && pnpm --filter=@databricks/appkit-ui dist",
+ "before:release": "pnpm build && pnpm --filter=docs build && pnpm --filter=@databricks/appkit dist && pnpm --filter=@databricks/appkit-ui dist",
"after:release": "npm publish packages/appkit/tmp --access public --provenance && npm publish packages/appkit-ui/tmp --access public --provenance"
},
"plugins": {
diff --git a/apps/dev-playground/client/src/appKitTypes.d.ts b/apps/dev-playground/client/src/appKitTypes.d.ts
index fc9dd815..0e0ae0b0 100644
--- a/apps/dev-playground/client/src/appKitTypes.d.ts
+++ b/apps/dev-playground/client/src/appKitTypes.d.ts
@@ -1,5 +1,5 @@
// Auto-generated by AppKit - DO NOT EDIT
-// Generated by 'npx appkit-generate-types' or Vite plugin during build
+// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build
import "@databricks/appkit-ui/react";
import type { SQLTypeMarker, SQLStringMarker, SQLNumberMarker, SQLBooleanMarker, SQLBinaryMarker, SQLDateMarker, SQLTimestampMarker } from "@databricks/appkit-ui/js";
diff --git a/apps/dev-playground/client/src/routes/type-safety.route.tsx b/apps/dev-playground/client/src/routes/type-safety.route.tsx
index d2df3ff9..07084c53 100644
--- a/apps/dev-playground/client/src/routes/type-safety.route.tsx
+++ b/apps/dev-playground/client/src/routes/type-safety.route.tsx
@@ -525,13 +525,13 @@ export default defineConfig({
diff --git a/docs/docs/development/llm-guide.mdx b/docs/docs/development/llm-guide.md
similarity index 100%
rename from docs/docs/development/llm-guide.mdx
rename to docs/docs/development/llm-guide.md
diff --git a/docs/docs/development/project-setup.mdx b/docs/docs/development/project-setup.md
similarity index 100%
rename from docs/docs/development/project-setup.mdx
rename to docs/docs/development/project-setup.md
diff --git a/docs/docs/development/type-generation.mdx b/docs/docs/development/type-generation.md
similarity index 89%
rename from docs/docs/development/type-generation.mdx
rename to docs/docs/development/type-generation.md
index 2de17706..ef277f12 100644
--- a/docs/docs/development/type-generation.mdx
+++ b/docs/docs/development/type-generation.md
@@ -42,13 +42,13 @@ export default defineConfig({
When the frontend is served through AppKit in dev mode, AppKit's dev server already includes `appKitTypesPlugin()` internally. You still want it in your client build pipeline if you run `vite build` separately.
-## CLI: `appkit-generate-types`
+## CLI: `npx @databricks/appkit generate-types`
For manual type generation or CI/CD pipelines, use the CLI command:
```bash
# Requires DATABRICKS_WAREHOUSE_ID (or pass as 3rd arg)
-npx appkit-generate-types [rootDir] [outFile] [warehouseId]
+npx @databricks/appkit generate-types [rootDir] [outFile] [warehouseId]
```
### Examples
@@ -56,19 +56,19 @@ npx appkit-generate-types [rootDir] [outFile] [warehouseId]
- Generate types using warehouse ID from environment
```bash
- npx appkit-generate-types . client/src/appKitTypes.d.ts
+ npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts
```
- Generate types using warehouse ID explicitly
```bash
- npx appkit-generate-types . client/src/appKitTypes.d.ts abc123...
+ npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts abc123...
```
- Force regeneration (skip cache)
```bash
- npx appkit-generate-types --no-cache
+ npx @databricks/appkit generate-types --no-cache
```
## How it works
diff --git a/package.json b/package.json
index 4b1fb018..db0eca29 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"format": "biome format --write .",
"lint:fix": "biome lint --write .",
"lint": "biome lint .",
- "pack:sdk": "pnpm build && pnpm -r tarball",
+ "pack:sdk": "pnpm build && pnpm --filter=docs build && pnpm -r tarball",
"prepare": "husky",
"release": "release-it",
"release:dry": "release-it --dry-run",
diff --git a/packages/appkit-ui/package.json b/packages/appkit-ui/package.json
index 37b95ccd..2a1776e2 100644
--- a/packages/appkit-ui/package.json
+++ b/packages/appkit-ui/package.json
@@ -11,8 +11,8 @@
"dist",
"bin",
"scripts",
+ "docs",
"CLAUDE.md",
- "AGENTS.md",
"llms.txt",
"README.md",
"DCO",
@@ -39,7 +39,7 @@
"clean:full": "rm -rf dist node_modules tmp",
"clean": "rm -rf dist tmp",
"dist": "tsx ../../tools/dist.ts",
- "tarball": "rm -rf tmp && tsx ../../tools/dist.ts && npm pack ./tmp --pack-destination ./tmp",
+ "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp",
"typecheck": "tsc --noEmit"
},
"dependencies": {
diff --git a/packages/appkit/bin/generate-types.js b/packages/appkit/bin/generate-types.js
deleted file mode 100755
index dabe63ba..00000000
--- a/packages/appkit/bin/generate-types.js
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env node
-import path from "node:path";
-
-import { generateFromEntryPoint } from "../dist/type-generator/index.js";
-
-// Parse arguments
-const args = process.argv.slice(2);
-const noCache = args.includes("--no-cache");
-const positionalArgs = args.filter((arg) => !arg.startsWith("--"));
-
-const rootDir = positionalArgs[0] || process.cwd();
-const outFile =
- positionalArgs[1] || path.join(process.cwd(), "client/src/appKitTypes.d.ts");
-
-const queryFolder = path.join(rootDir, "config/queries");
-
-const warehouseId = positionalArgs[2] || process.env.DATABRICKS_WAREHOUSE_ID;
-if (!warehouseId) {
- throw new Error("DATABRICKS_WAREHOUSE_ID is not set");
-}
-
-await generateFromEntryPoint({
- queryFolder,
- outFile,
- warehouseId,
- noCache,
-});
diff --git a/packages/appkit/package.json b/packages/appkit/package.json
index 718a2fac..5153fb99 100644
--- a/packages/appkit/package.json
+++ b/packages/appkit/package.json
@@ -13,9 +13,9 @@
"dist",
"bin",
"scripts",
+ "docs",
"CLAUDE.md",
"llms.txt",
- "AGENTS.md",
"README.md",
"DCO",
"NOTICE.md"
@@ -25,23 +25,23 @@
"development": "./src/index.ts",
"default": "./dist/index.js"
},
+ "./type-generator": {
+ "types": "./dist/type-generator/index.d.ts",
+ "development": "./src/type-generator/index.ts",
+ "default": "./dist/type-generator/index.js"
+ },
"./package.json": "./package.json"
},
- "bin": {
- "appkit-generate-types": "./bin/generate-types.js",
- "appkit-lint": "./bin/appkit-lint.js"
- },
"scripts": {
"build:package": "tsdown --config tsdown.config.ts",
"build:watch": "tsdown --config tsdown.config.ts --watch",
"clean:full": "rm -rf dist node_modules tmp",
"clean": "rm -rf dist tmp",
"dist": "tsx ../../tools/dist.ts",
- "tarball": "rm -rf tmp && tsx ../../tools/dist.ts && npm pack ./tmp --pack-destination ./tmp",
+ "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@ast-grep/napi": "^0.37.0",
"@databricks/sdk-experimental": "^0.15.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/api-logs": "^0.208.0",
@@ -81,6 +81,7 @@
"publishConfig": {
"exports": {
".": "./dist/index.js",
+ "./type-generator": "./dist/type-generator/index.js",
"./package.json": "./package.json"
}
}
diff --git a/packages/appkit/src/index.ts b/packages/appkit/src/index.ts
index 1c055071..f020c097 100644
--- a/packages/appkit/src/index.ts
+++ b/packages/appkit/src/index.ts
@@ -42,5 +42,5 @@ export {
type TelemetryConfig,
} from "./telemetry";
-// Vite plugin
+// Vite plugin and type generation
export { appKitTypesPlugin } from "./type-generator/vite-plugin";
diff --git a/packages/appkit/src/type-generator/index.ts b/packages/appkit/src/type-generator/index.ts
index 4ecd0b56..c41bc577 100644
--- a/packages/appkit/src/type-generator/index.ts
+++ b/packages/appkit/src/type-generator/index.ts
@@ -28,7 +28,7 @@ function generateTypeDeclarations(querySchemas: QuerySchema[] = []): string {
const querySection = queryEntries ? `\n${queryEntries};\n ` : "";
return `// Auto-generated by AppKit - DO NOT EDIT
-// Generated by 'npx appkit-generate-types' or Vite plugin during build
+// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build
import "@databricks/appkit-ui/react";
import type { SQLTypeMarker, SQLStringMarker, SQLNumberMarker, SQLBooleanMarker, SQLBinaryMarker, SQLDateMarker, SQLTimestampMarker } from "@databricks/appkit-ui/js";
diff --git a/packages/shared/bin/appkit.js b/packages/shared/bin/appkit.js
new file mode 100644
index 00000000..35b00ed4
--- /dev/null
+++ b/packages/shared/bin/appkit.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+import "../dist/cli/index.js";
diff --git a/packages/shared/package.json b/packages/shared/package.json
index cc43ae46..d7558056 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -8,6 +8,10 @@
"development": "./src/index.ts",
"default": "./dist/index.js"
},
+ "./cli": {
+ "development": "./src/cli/index.ts",
+ "default": "./dist/cli/index.js"
+ },
"./package.json": "./package.json"
},
"scripts": {
@@ -29,10 +33,12 @@
"publishConfig": {
"exports": {
".": "./dist/index.js",
+ "./cli": "./dist/cli/index.js",
"./package.json": "./package.json"
}
},
"dependencies": {
- "fast-glob": "^3.3.3"
+ "@ast-grep/napi": "^0.37.0",
+ "commander": "^12.1.0"
}
}
diff --git a/packages/shared/scripts/postinstall.js b/packages/shared/scripts/postinstall.js
index 3db18ec1..22f1c09d 100644
--- a/packages/shared/scripts/postinstall.js
+++ b/packages/shared/scripts/postinstall.js
@@ -2,5 +2,5 @@
console.log("");
console.log("[@databricks/appkit] To setup AI assistant instructions, run:");
console.log("");
-console.log(" npx appkit-setup --write");
+console.log(" npx appkit setup --write");
console.log("");
diff --git a/packages/shared/src/cli/commands/docs.ts b/packages/shared/src/cli/commands/docs.ts
new file mode 100644
index 00000000..cac88afd
--- /dev/null
+++ b/packages/shared/src/cli/commands/docs.ts
@@ -0,0 +1,66 @@
+import { Command } from "commander";
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+function findPackageRoot(): string {
+ let dir = __dirname;
+ while (dir !== path.parse(dir).root) {
+ if (fs.existsSync(path.join(dir, "package.json"))) {
+ return dir;
+ }
+ dir = path.dirname(dir);
+ }
+ throw new Error("Could not find package root");
+}
+
+function runDocs(docPath?: string) {
+ const packageRoot = findPackageRoot();
+
+ if (!docPath) {
+ // Display llms.txt by default
+ const llmsPath = path.join(packageRoot, "llms.txt");
+
+ if (!fs.existsSync(llmsPath)) {
+ console.error("Error: llms.txt not found in package");
+ process.exit(1);
+ }
+
+ const content = fs.readFileSync(llmsPath, "utf-8");
+ console.log(content);
+ return;
+ }
+
+ // Handle path - remove leading ./ and / first, then strip prefixes
+ let normalizedPath = docPath;
+
+ // Strip leading ./ or /
+ normalizedPath = normalizedPath.replace(/^\.\//, "");
+ normalizedPath = normalizedPath.replace(/^\//, "");
+
+ // Remove /appkit/docs/ or docs/ prefix since files are in packageRoot/docs/
+ normalizedPath = normalizedPath.replace(/^appkit\/docs\//, "");
+ normalizedPath = normalizedPath.replace(/^docs\//, "");
+
+ const fullPath = path.join(packageRoot, "docs", normalizedPath);
+
+ if (!fs.existsSync(fullPath)) {
+ console.error(`Error: Documentation file not found: ${docPath}`);
+ console.error(`Tried: ${fullPath}`);
+ process.exit(1);
+ }
+
+ const content = fs.readFileSync(fullPath, "utf-8");
+ console.log(content);
+}
+
+export const docsCommand = new Command("docs")
+ .description("Display embedded documentation")
+ .argument(
+ "[path]",
+ "Path to specific documentation file (e.g., /appkit/docs/api/appkit-ui/components/Sidebar.md)",
+ )
+ .action(runDocs);
diff --git a/packages/shared/src/cli/commands/generate-types.ts b/packages/shared/src/cli/commands/generate-types.ts
new file mode 100644
index 00000000..9c81a3a8
--- /dev/null
+++ b/packages/shared/src/cli/commands/generate-types.ts
@@ -0,0 +1,65 @@
+import { Command } from "commander";
+import path from "node:path";
+
+/**
+ * Generate types command implementation
+ */
+async function runGenerateTypes(
+ rootDir?: string,
+ outFile?: string,
+ warehouseId?: string,
+ options?: { noCache?: boolean },
+) {
+ try {
+ // Try to import the type generator from @databricks/appkit
+ const { generateFromEntryPoint } = await import(
+ "@databricks/appkit/type-generator"
+ );
+
+ const resolvedRootDir = rootDir || process.cwd();
+ const resolvedOutFile =
+ outFile || path.join(process.cwd(), "client/src/appKitTypes.d.ts");
+
+ const queryFolder = path.join(resolvedRootDir, "config/queries");
+
+ const resolvedWarehouseId =
+ warehouseId || process.env.DATABRICKS_WAREHOUSE_ID;
+ if (!resolvedWarehouseId) {
+ console.error(
+ "Error: DATABRICKS_WAREHOUSE_ID is not set. Please provide it as an argument or environment variable.",
+ );
+ process.exit(1);
+ }
+
+ await generateFromEntryPoint({
+ queryFolder,
+ outFile: resolvedOutFile,
+ warehouseId: resolvedWarehouseId,
+ noCache: options?.noCache || false,
+ });
+ } catch (error) {
+ if (
+ error instanceof Error &&
+ error.message.includes("Cannot find module")
+ ) {
+ console.error(
+ "Error: The 'generate-types' command is only available in @databricks/appkit.",
+ );
+ console.error("Please install @databricks/appkit to use this command.");
+ process.exit(1);
+ }
+ throw error;
+ }
+}
+
+export const generateTypesCommand = new Command("generate-types")
+ .description("Generate TypeScript types from SQL queries")
+ .argument("[rootDir]", "Root directory of the project", process.cwd())
+ .argument(
+ "[outFile]",
+ "Output file path",
+ path.join(process.cwd(), "client/src/appKitTypes.d.ts"),
+ )
+ .argument("[warehouseId]", "Databricks warehouse ID")
+ .option("--no-cache", "Disable caching for type generation")
+ .action(runGenerateTypes);
diff --git a/packages/appkit/bin/appkit-lint.js b/packages/shared/src/cli/commands/lint.ts
old mode 100755
new mode 100644
similarity index 79%
rename from packages/appkit/bin/appkit-lint.js
rename to packages/shared/src/cli/commands/lint.ts
index 22ce2661..9a23acc6
--- a/packages/appkit/bin/appkit-lint.js
+++ b/packages/shared/src/cli/commands/lint.ts
@@ -1,14 +1,17 @@
-#!/usr/bin/env node
-/**
- * AST-based linting using ast-grep.
- * Catches patterns that ESLint/TypeScript miss or handle poorly.
- * Usage: npx appkit-lint
- */
+import { Command } from "commander";
import { parse, Lang } from "@ast-grep/napi";
import fs from "node:fs";
import path from "node:path";
-const rules = [
+interface Rule {
+ id: string;
+ pattern: string;
+ message: string;
+ includeTests?: boolean;
+ filter?: (code: string) => boolean;
+}
+
+const rules: Rule[] = [
{
id: "no-double-type-assertion",
pattern: "$X as unknown as $Y",
@@ -37,13 +40,13 @@ const rules = [
},
];
-function isTestFile(filePath) {
+function isTestFile(filePath: string): boolean {
return (
/\.(test|spec)\.(ts|tsx)$/.test(filePath) || filePath.includes("/tests/")
);
}
-function findTsFiles(dir, files = []) {
+function findTsFiles(dir: string, files: string[] = []): string[] {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
@@ -61,8 +64,17 @@ function findTsFiles(dir, files = []) {
return files;
}
-function lintFile(filePath, rules) {
- const violations = [];
+interface Violation {
+ file: string;
+ line: number;
+ column: number;
+ rule: string;
+ message: string;
+ code: string;
+}
+
+function lintFile(filePath: string, rules: Rule[]): Violation[] {
+ const violations: Violation[] = [];
const content = fs.readFileSync(filePath, "utf-8");
const lang = filePath.endsWith(".tsx") ? Lang.Tsx : Lang.TypeScript;
const testFile = isTestFile(filePath);
@@ -96,13 +108,16 @@ function lintFile(filePath, rules) {
return violations;
}
-function main() {
+/**
+ * Lint command implementation
+ */
+function runLint() {
const rootDir = process.cwd();
const files = findTsFiles(rootDir);
console.log(`Scanning ${files.length} TypeScript files...\n`);
- const allViolations = [];
+ const allViolations: Violation[] = [];
for (const file of files) {
const violations = lintFile(file, rules);
@@ -126,4 +141,6 @@ function main() {
process.exit(1);
}
-main();
+export const lintCommand = new Command("lint")
+ .description("Run AST-based linting on TypeScript files")
+ .action(runLint);
diff --git a/packages/shared/bin/setup-claude.js b/packages/shared/src/cli/commands/setup.ts
similarity index 72%
rename from packages/shared/bin/setup-claude.js
rename to packages/shared/src/cli/commands/setup.ts
index cfbb669c..b6eaece8 100644
--- a/packages/shared/bin/setup-claude.js
+++ b/packages/shared/src/cli/commands/setup.ts
@@ -1,16 +1,4 @@
-#!/usr/bin/env node
-
-/**
- * CLI tool to setup CLAUDE.md for Databricks AppKit packages.
- *
- * This bin is included in both @databricks/appkit and @databricks/appkit-ui
- * so it's available regardless of which package the user installs.
- *
- * Usage:
- * npx appkit-setup # Show detected packages and content
- * npx appkit-setup --write # Create or update CLAUDE.md file
- */
-
+import { Command } from "commander";
import fs from "node:fs";
import path from "node:path";
@@ -26,15 +14,20 @@ const SECTION_START = "";
const SECTION_END = "";
/**
- * Find which AppKit packages are installed by checking for CLAUDE.md
+ * Find which AppKit packages are installed by checking for package.json
*/
function findInstalledPackages() {
const cwd = process.cwd();
const installed = [];
for (const pkg of PACKAGES) {
- const claudePath = path.join(cwd, "node_modules", pkg.name, "package.json");
- if (fs.existsSync(claudePath)) {
+ const packagePath = path.join(
+ cwd,
+ "node_modules",
+ pkg.name,
+ "package.json",
+ );
+ if (fs.existsSync(packagePath)) {
installed.push(pkg);
}
}
@@ -45,7 +38,7 @@ function findInstalledPackages() {
/**
* Generate the AppKit section content
*/
-function generateSection(packages) {
+function generateSection(packages: typeof PACKAGES) {
const links = packages
.map((pkg) => {
const docPath = `./node_modules/${pkg.name}/CLAUDE.md`;
@@ -65,7 +58,7 @@ ${SECTION_END}`;
/**
* Generate standalone CLAUDE.md content (when no existing file)
*/
-function generateStandalone(packages) {
+function generateStandalone(packages: typeof PACKAGES) {
const links = packages
.map((pkg) => {
const docPath = `./node_modules/${pkg.name}/CLAUDE.md`;
@@ -88,7 +81,7 @@ ${SECTION_END}
/**
* Update existing content with AppKit section
*/
-function updateContent(existingContent, packages) {
+function updateContent(existingContent: string, packages: typeof PACKAGES) {
const newSection = generateSection(packages);
// Check if AppKit section already exists
@@ -107,27 +100,10 @@ function updateContent(existingContent, packages) {
}
/**
- * Main CLI logic
+ * Setup command implementation
*/
-function main() {
- const args = process.argv.slice(2);
- const shouldWrite = args.includes("--write") || args.includes("-w");
- const help = args.includes("--help") || args.includes("-h");
-
- if (help) {
- console.log(`
-Usage: npx appkit-setup [options]
-
-Options:
- --write, -w Create or update CLAUDE.md file in current directory
- --help, -h Show this help message
-
-Examples:
- npx appkit-setup # Show detected packages and preview content
- npx appkit-setup --write # Create or update CLAUDE.md
-`);
- return;
- }
+function runSetup(options: { write?: boolean }) {
+ const shouldWrite = options.write;
// Find installed packages
const installed = findInstalledPackages();
@@ -151,8 +127,8 @@ Examples:
? fs.readFileSync(claudePath, "utf-8")
: null;
- let finalContent;
- let action;
+ let finalContent: string;
+ let action: string;
if (existingContent) {
finalContent = updateContent(existingContent, installed);
@@ -168,7 +144,7 @@ Examples:
console.log(` Path: ${claudePath}`);
} else {
console.log("\nTo create/update CLAUDE.md, run:");
- console.log(" npx appkit-setup --write\n");
+ console.log(" npx appkit setup --write\n");
if (existingContent) {
console.log(
@@ -187,4 +163,7 @@ Examples:
}
}
-main();
+export const setupCommand = new Command("setup")
+ .description("Setup CLAUDE.md with AppKit package references")
+ .option("-w, --write", "Create or update CLAUDE.md file in current directory")
+ .action(runSetup);
diff --git a/packages/shared/src/cli/commands/type-generator.d.ts b/packages/shared/src/cli/commands/type-generator.d.ts
new file mode 100644
index 00000000..debda666
--- /dev/null
+++ b/packages/shared/src/cli/commands/type-generator.d.ts
@@ -0,0 +1,9 @@
+// Type declarations for optional @databricks/appkit/type-generator module
+declare module "@databricks/appkit/type-generator" {
+ export function generateFromEntryPoint(options: {
+ queryFolder: string;
+ outFile: string;
+ warehouseId: string;
+ noCache: boolean;
+ }): Promise;
+}
diff --git a/packages/shared/src/cli/index.ts b/packages/shared/src/cli/index.ts
new file mode 100644
index 00000000..7ab2eecf
--- /dev/null
+++ b/packages/shared/src/cli/index.ts
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+
+import { Command } from "commander";
+import { setupCommand } from "./commands/setup.js";
+import { generateTypesCommand } from "./commands/generate-types.js";
+import { lintCommand } from "./commands/lint.js";
+import { docsCommand } from "./commands/docs.js";
+
+const cmd = new Command();
+
+cmd.name("appkit").description("CLI tools for Databricks AppKit");
+
+cmd.addCommand(setupCommand);
+cmd.addCommand(generateTypesCommand);
+cmd.addCommand(lintCommand);
+cmd.addCommand(docsCommand);
+
+cmd.parse();
diff --git a/packages/shared/tsdown.config.ts b/packages/shared/tsdown.config.ts
index 57384635..b1fdb9c5 100644
--- a/packages/shared/tsdown.config.ts
+++ b/packages/shared/tsdown.config.ts
@@ -2,17 +2,18 @@ import { defineConfig } from "tsdown";
export default defineConfig({
name: "shared",
- entry: "src/index.ts",
+ entry: ["src/index.ts", "src/cli/index.ts"],
outDir: "dist",
minify: false,
format: "esm",
- platform: "neutral",
+ platform: "node", // Required for bin commands
sourcemap: false,
unbundle: true,
dts: true,
clean: false,
hash: false,
skipNodeModulesBundle: true,
+ external: [/^@databricks\//],
tsconfig: "./tsconfig.json",
exports: {
devExports: "development",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2f5662d6..b9b2bcae 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -233,9 +233,6 @@ importers:
packages/appkit:
dependencies:
- '@ast-grep/napi':
- specifier: ^0.37.0
- version: 0.37.0
'@databricks/sdk-experimental':
specifier: ^0.15.0
version: 0.15.0
@@ -493,9 +490,12 @@ importers:
packages/shared:
dependencies:
- fast-glob:
- specifier: ^3.3.3
- version: 3.3.3
+ '@ast-grep/napi':
+ specifier: ^0.37.0
+ version: 0.37.0
+ commander:
+ specifier: ^12.1.0
+ version: 12.1.0
devDependencies:
'@types/dependency-tree':
specifier: ^8.1.4
diff --git a/template/package.json b/template/package.json
index 71db79cd..918f5e55 100644
--- a/template/package.json
+++ b/template/package.json
@@ -12,7 +12,7 @@
"typecheck": "tsc -p ./tsconfig.server.json --noEmit && tsc -p ./tsconfig.client.json --noEmit",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
- "lint:ast-grep": "appkit-lint",
+ "lint:ast-grep": "appkit lint",
"format": "prettier --check .",
"format:fix": "prettier --write .",
"test": "vitest run && npm run test:smoke",
@@ -20,8 +20,8 @@
"test:e2e:ui": "playwright test --ui",
"test:smoke": "playwright install chromium && playwright test tests/smoke.spec.ts",
"clean": "rm -rf client/dist dist build node_modules .smoke-test test-results playwright-report",
- "typegen": "appkit-generate-types",
- "setup": "appkit-setup --write"
+ "typegen": "appkit generate-types",
+ "setup": "appkit setup --write"
},
"keywords": [],
"author": "",
diff --git a/tools/dist.ts b/tools/dist.ts
index 9faeee6f..7ff61958 100644
--- a/tools/dist.ts
+++ b/tools/dist.ts
@@ -3,6 +3,11 @@ import path from "node:path";
const __dirname = path.dirname(new URL(import.meta.url).pathname);
+// Read CLI dependencies from shared package
+const sharedPkgPath = path.join(__dirname, "../packages/shared/package.json");
+const sharedPkg = JSON.parse(fs.readFileSync(sharedPkgPath, "utf-8"));
+const CLI_DEPENDENCIES = sharedPkg.dependencies;
+
fs.mkdirSync("tmp", { recursive: true });
const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8"));
@@ -13,25 +18,26 @@ pkg.exports = pkg.publishConfig.exports;
delete pkg.publishConfig.exports;
const isAppKitPackage = pkg.name?.startsWith("@databricks/appkit");
-const sharedBin = path.join(
- __dirname,
- "../packages/shared/bin/setup-claude.js",
-);
+const sharedBin = path.join(__dirname, "../packages/shared/bin/appkit.js");
const sharedPostinstall = path.join(
__dirname,
"../packages/shared/scripts/postinstall.js",
);
-// Add appkit-setup bin and postinstall for @databricks/appkit* packages
+// Add appkit bin and postinstall for @databricks/appkit* packages
if (isAppKitPackage) {
if (fs.existsSync(sharedBin)) {
pkg.bin = pkg.bin || {};
- pkg.bin["appkit-setup"] = "./bin/setup-claude.js";
+ pkg.bin.appkit = "./bin/appkit.js";
}
if (fs.existsSync(sharedPostinstall)) {
pkg.scripts = pkg.scripts || {};
pkg.scripts.postinstall = "node scripts/postinstall.js";
}
+
+ // Add CLI dependencies from shared package (required for bin commands to work)
+ pkg.dependencies = pkg.dependencies || {};
+ Object.assign(pkg.dependencies, CLI_DEPENDENCIES);
}
fs.writeFileSync("tmp/package.json", JSON.stringify(pkg, null, 2));
@@ -46,7 +52,15 @@ if (fs.existsSync("bin")) {
if (isAppKitPackage) {
if (fs.existsSync(sharedBin)) {
fs.mkdirSync("tmp/bin", { recursive: true });
- fs.copyFileSync(sharedBin, "tmp/bin/setup-claude.js");
+ fs.copyFileSync(sharedBin, "tmp/bin/appkit.js");
+
+ // Copy CLI code from shared/dist/cli to tmp/dist/cli
+ const sharedCliDist = path.join(__dirname, "../packages/shared/dist/cli");
+ if (fs.existsSync(sharedCliDist)) {
+ const tmpCliDist = "tmp/dist/cli";
+ fs.mkdirSync(tmpCliDist, { recursive: true });
+ fs.cpSync(sharedCliDist, tmpCliDist, { recursive: true });
+ }
}
if (fs.existsSync(sharedPostinstall)) {
fs.mkdirSync("tmp/scripts", { recursive: true });
@@ -54,15 +68,63 @@ if (isAppKitPackage) {
}
}
-if (fs.existsSync("llms.txt")) {
- fs.copyFileSync("llms.txt", "tmp/llms.txt");
-} else {
- fs.copyFileSync(path.join(__dirname, "../llms.txt"), "tmp/llms.txt");
+// Copy documentation from docs/build into tmp/docs/
+const docsBuildPath = path.join(__dirname, "../docs/build");
+
+// Copy all .md files and docs/ subdirectory from docs/build to tmp/docs
+fs.mkdirSync("tmp/docs", { recursive: true });
+
+// Copy all files and directories we want, preserving structure
+const itemsToCopy = fs.readdirSync(docsBuildPath);
+for (const item of itemsToCopy) {
+ const sourcePath = path.join(docsBuildPath, item);
+ const stat = fs.statSync(sourcePath);
+
+ // Copy .md files and docs directory
+ if (item.endsWith(".md") || item === "docs") {
+ const destPath = path.join("tmp/docs", item);
+ if (stat.isDirectory()) {
+ fs.cpSync(sourcePath, destPath, { recursive: true });
+ } else {
+ fs.copyFileSync(sourcePath, destPath);
+ }
+ }
}
-// Copy llms.txt as CLAUDE.md and AGENTS.md (npm pack doesn't support symlinks)
+// Process llms.txt (keep existing logic but update path replacement)
+const llmsSourcePath = path.join(docsBuildPath, "llms.txt");
+let llmsContent = fs.readFileSync(llmsSourcePath, "utf-8");
+
+// Replace /appkit/ with ./docs/ to match new structure
+llmsContent = llmsContent.replace(/\/appkit\//g, "./docs/");
+
+// Prepend AI agent guidance for navigating documentation
+const agentGuidance = `## For AI Agents/Assistants
+
+To view specific documentation files referenced below, use the appkit CLI:
+
+\`\`\`bash
+npx @databricks/appkit docs
+\`\`\`
+
+Examples:
+- View main documentation: \`npx @databricks/appkit docs\`
+- View specific file: \`npx @databricks/appkit docs ./docs/docs.md\`
+- View API reference: \`npx @databricks/appkit docs ./docs/docs/api.md\`
+- View component docs: \`npx @databricks/appkit docs ./docs/docs/api/appkit-ui/components/Sidebar.md\`
+
+The CLI will display the documentation content directly in the terminal.
+
+---
+
+`;
+
+llmsContent = agentGuidance + llmsContent;
+
+fs.writeFileSync("tmp/llms.txt", llmsContent);
+
+// Copy llms.txt as CLAUDE.md (npm pack doesn't support symlinks)
fs.copyFileSync("tmp/llms.txt", "tmp/CLAUDE.md");
-fs.copyFileSync("tmp/llms.txt", "tmp/AGENTS.md");
fs.copyFileSync(path.join(__dirname, "../README.md"), "tmp/README.md");
fs.copyFileSync(path.join(__dirname, "../LICENSE"), "tmp/LICENSE");
diff --git a/tools/generate-component-mdx.ts b/tools/generate-component-mdx.ts
index 4e3b8357..1f11bac7 100644
--- a/tools/generate-component-mdx.ts
+++ b/tools/generate-component-mdx.ts
@@ -562,7 +562,10 @@ function main() {
const entries = fs.readdirSync(outputDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
- fs.rmSync(path.join(outputDir, entry.name), { recursive: true });
+ fs.rmSync(path.join(outputDir, entry.name), {
+ recursive: true,
+ force: true,
+ });
}
}
}