Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"includes": [
"**",
"!**/dist",
"!**/tmp",
"!**/*.d.ts",
"!**/build",
"!**/coverage",
Expand Down
3 changes: 3 additions & 0 deletions packages/app-kit-ui/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLAUDE.md - @databricks/appkit-ui

## TBD
3 changes: 3 additions & 0 deletions packages/app-kit-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"packageManager": "pnpm@10.21.0",
"files": [
"dist",
"bin",
"scripts",
"CLAUDE.md",
"llms.txt",
"README.md",
"DCO",
Expand Down
3 changes: 3 additions & 0 deletions packages/app-kit/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLAUDE.md - @databricks/appkit

## TBD
3 changes: 3 additions & 0 deletions packages/app-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"packageManager": "pnpm@10.21.0",
"files": [
"dist",
"bin",
"scripts",
"CLAUDE.md",
"llms.txt",
"README.md",
"DCO",
Expand Down
190 changes: 190 additions & 0 deletions packages/shared/bin/setup-claude.js
Original file line number Diff line number Diff line change
@@ -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 = "<!-- appkit-instructions-start -->";
const SECTION_END = "<!-- appkit-instructions-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();
6 changes: 6 additions & 0 deletions packages/shared/scripts/postinstall.js
Original file line number Diff line number Diff line change
@@ -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("");
45 changes: 44 additions & 1 deletion tools/dist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down