diff --git a/biome.json b/biome.json index 6da1036..bf1c41d 100644 --- a/biome.json +++ b/biome.json @@ -10,6 +10,7 @@ "includes": [ "**", "!**/dist", + "!**/tmp", "!**/*.d.ts", "!**/build", "!**/coverage", diff --git a/packages/app-kit-ui/CLAUDE.md b/packages/app-kit-ui/CLAUDE.md new file mode 100644 index 0000000..8ffdd96 --- /dev/null +++ b/packages/app-kit-ui/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md - @databricks/appkit-ui + +## TBD \ No newline at end of file diff --git a/packages/app-kit-ui/package.json b/packages/app-kit-ui/package.json index 031e7e4..1505fb4 100644 --- a/packages/app-kit-ui/package.json +++ b/packages/app-kit-ui/package.json @@ -5,6 +5,9 @@ "packageManager": "pnpm@10.21.0", "files": [ "dist", + "bin", + "scripts", + "CLAUDE.md", "llms.txt", "README.md", "DCO", diff --git a/packages/app-kit/CLAUDE.md b/packages/app-kit/CLAUDE.md new file mode 100644 index 0000000..d17f226 --- /dev/null +++ b/packages/app-kit/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md - @databricks/appkit + +## TBD \ No newline at end of file diff --git a/packages/app-kit/package.json b/packages/app-kit/package.json index b8324f2..2bc95e3 100644 --- a/packages/app-kit/package.json +++ b/packages/app-kit/package.json @@ -7,6 +7,9 @@ "packageManager": "pnpm@10.21.0", "files": [ "dist", + "bin", + "scripts", + "CLAUDE.md", "llms.txt", "README.md", "DCO", diff --git a/packages/shared/bin/setup-claude.js b/packages/shared/bin/setup-claude.js new file mode 100644 index 0000000..b0edd96 --- /dev/null +++ b/packages/shared/bin/setup-claude.js @@ -0,0 +1,190 @@ +#!/usr/bin/env node + +/** + * CLI tool to setup CLAUDE.md for Databricks AppKit packages. + * + * This bin is included in both @databricks/app-kit and @databricks/app-kit-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 fs from "node:fs"; +import path from "node:path"; + +const PACKAGES = [ + { name: "@databricks/app-kit", description: "Backend SDK" }, + { + name: "@databricks/app-kit-ui", + description: "UI Integration, Charts, Tables, SSE, and more.", + }, +]; + +const SECTION_START = ""; +const SECTION_END = ""; + +/** + * Find which AppKit packages are installed by checking for CLAUDE.md + */ +function findInstalledPackages() { + const cwd = process.cwd(); + const installed = []; + + for (const pkg of PACKAGES) { + const claudePath = path.join(cwd, "node_modules", pkg.name, "CLAUDE.md"); + if (fs.existsSync(claudePath)) { + installed.push(pkg); + } + } + + return installed; +} + +/** + * Generate the AppKit section content + */ +function generateSection(packages) { + const links = packages + .map((pkg) => { + const docPath = `./node_modules/${pkg.name}/CLAUDE.md`; + return `- **${pkg.name}** (${pkg.description}): [${docPath}](${docPath})`; + }) + .join("\n"); + + return `${SECTION_START} +## Databricks AppKit + +This project uses Databricks AppKit packages. For AI assistant guidance on using these packages, refer to: + +${links} +${SECTION_END}`; +} + +/** + * Generate standalone CLAUDE.md content (when no existing file) + */ +function generateStandalone(packages) { + const links = packages + .map((pkg) => { + const docPath = `./node_modules/${pkg.name}/CLAUDE.md`; + return `- **${pkg.name}** (${pkg.description}): [${docPath}](${docPath})`; + }) + .join("\n"); + + return `# AI Assistant Instructions + +${SECTION_START} +## Databricks AppKit + +This project uses Databricks AppKit packages. For AI assistant guidance on using these packages, refer to: + +${links} +${SECTION_END} +`; +} + +/** + * Update existing content with AppKit section + */ +function updateContent(existingContent, packages) { + const newSection = generateSection(packages); + + // Check if AppKit section already exists + const startIndex = existingContent.indexOf(SECTION_START); + const endIndex = existingContent.indexOf(SECTION_END); + + if (startIndex !== -1 && endIndex !== -1) { + // Replace existing section + const before = existingContent.substring(0, startIndex); + const after = existingContent.substring(endIndex + SECTION_END.length); + return before + newSection + after; + } + + // Append section to end + return `${existingContent.trimEnd()}\n\n${newSection}\n`; +} + +/** + * Main CLI logic + */ +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; + } + + // Find installed packages + const installed = findInstalledPackages(); + + if (installed.length === 0) { + console.log("No @databricks/app-kit packages found in node_modules."); + console.log("\nMake sure you've installed at least one of:"); + PACKAGES.forEach((pkg) => { + console.log(` - ${pkg.name}`); + }); + process.exit(1); + } + + console.log("Detected packages:"); + installed.forEach((pkg) => { + console.log(` āœ“ ${pkg.name}`); + }); + + const claudePath = path.join(process.cwd(), "CLAUDE.md"); + const existingContent = fs.existsSync(claudePath) + ? fs.readFileSync(claudePath, "utf-8") + : null; + + let finalContent; + let action; + + if (existingContent) { + finalContent = updateContent(existingContent, installed); + action = existingContent.includes(SECTION_START) ? "Updated" : "Added to"; + } else { + finalContent = generateStandalone(installed); + action = "Created"; + } + + if (shouldWrite) { + fs.writeFileSync(claudePath, finalContent); + console.log(`\nāœ“ ${action} CLAUDE.md`); + console.log(` Path: ${claudePath}`); + } else { + console.log("\nTo create/update CLAUDE.md, run:"); + console.log(" npx appkit-setup --write\n"); + + if (existingContent) { + console.log( + `This will ${ + existingContent.includes(SECTION_START) + ? "update the existing" + : "add a new" + } AppKit section.\n`, + ); + } + + console.log("Preview of AppKit section:\n"); + console.log("─".repeat(50)); + console.log(generateSection(installed)); + console.log("─".repeat(50)); + } +} + +main(); diff --git a/packages/shared/scripts/postinstall.js b/packages/shared/scripts/postinstall.js new file mode 100644 index 0000000..3db18ec --- /dev/null +++ b/packages/shared/scripts/postinstall.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node +console.log(""); +console.log("[@databricks/appkit] To setup AI assistant instructions, run:"); +console.log(""); +console.log(" npx appkit-setup --write"); +console.log(""); diff --git a/tools/dist.ts b/tools/dist.ts index 3bd26cf..81ef265 100644 --- a/tools/dist.ts +++ b/tools/dist.ts @@ -12,11 +12,54 @@ delete pkg.dependencies.shared; pkg.exports = pkg.publishConfig.exports; delete pkg.publishConfig.exports; +const isAppKitPackage = pkg.name?.startsWith("@databricks/app-kit"); +const sharedBin = path.join( + __dirname, + "../packages/shared/bin/setup-claude.js", +); +const sharedPostinstall = path.join( + __dirname, + "../packages/shared/scripts/postinstall.js", +); + +// Add appkit-setup bin and postinstall for @databricks/app-kit* packages +if (isAppKitPackage) { + if (fs.existsSync(sharedBin)) { + pkg.bin = pkg.bin || {}; + pkg.bin["appkit-setup"] = "./bin/setup-claude.js"; + } + if (fs.existsSync(sharedPostinstall)) { + pkg.scripts = pkg.scripts || {}; + pkg.scripts.postinstall = "node scripts/postinstall.js"; + } +} + fs.writeFileSync("tmp/package.json", JSON.stringify(pkg, null, 2)); fs.cpSync("dist", "tmp/dist", { recursive: true }); -fs.copyFileSync(path.join(__dirname, "../llms.txt"), "tmp/llms.txt"); +// Copy bin and scripts from shared package +if (isAppKitPackage) { + if (fs.existsSync(sharedBin)) { + fs.mkdirSync("tmp/bin", { recursive: true }); + fs.copyFileSync(sharedBin, "tmp/bin/setup-claude.js"); + } + if (fs.existsSync(sharedPostinstall)) { + fs.mkdirSync("tmp/scripts", { recursive: true }); + fs.copyFileSync(sharedPostinstall, "tmp/scripts/postinstall.js"); + } +} + +if (fs.existsSync("CLAUDE.md")) { + fs.copyFileSync("CLAUDE.md", "tmp/CLAUDE.md"); +} + +if (fs.existsSync("llms.txt")) { + fs.copyFileSync("llms.txt", "tmp/llms.txt"); +} else { + fs.copyFileSync(path.join(__dirname, "../llms.txt"), "tmp/llms.txt"); +} + fs.copyFileSync(path.join(__dirname, "../README.md"), "tmp/README.md"); fs.copyFileSync(path.join(__dirname, "../LICENSE"), "tmp/LICENSE"); fs.copyFileSync(path.join(__dirname, "../DCO"), "tmp/DCO");