Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
36 changes: 34 additions & 2 deletions create-bestax/src/__tests__/project-creator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,10 @@ describe('ProjectCreator', () => {
>
).mockResolvedValue(true);

await projectCreator.setupSkills('/target/path', 'my-app');
await projectCreator.setupSkills('/target/path', 'my-app', {
bulmaFlavor: 'complete',
iconLibrary: 'none',
});

expect(fs.default.copy).toHaveBeenCalledWith(
expect.stringContaining('skills'),
Expand All @@ -231,6 +234,32 @@ describe('ProjectCreator', () => {
expect.stringContaining('CLAUDE.md'),
expect.stringContaining('my-app')
);
const written = (
fs.default.writeFile as unknown as jest.Mock
).mock.calls.find(([p]) => String(p).includes('CLAUDE.md'))?.[1];
expect(written).toContain('Bulma flavor: **complete**');
expect(written).toContain('No icon library is installed');
expect(written).not.toContain('classPrefix');
});

it('documents the prefix caveat and icon library for a prefixed scaffold', async () => {
(
fileSystem.checkDirectoryExists as jest.MockedFunction<
typeof fileSystem.checkDirectoryExists
>
).mockResolvedValue(true);

await projectCreator.setupSkills('/target/path', 'my-app', {
bulmaFlavor: 'prefixed',
iconLibrary: 'fontawesome',
});

const written = (
fs.default.writeFile as unknown as jest.Mock
).mock.calls.find(([p]) => String(p).includes('CLAUDE.md'))?.[1];
expect(written).toContain('classPrefix="bestax-"');
expect(written).toContain('bestax-prefixed.css');
expect(written).toContain('Icon library: **Font Awesome**');
});

it('skips when the skills bundle is missing', async () => {
Expand All @@ -240,7 +269,10 @@ describe('ProjectCreator', () => {
>
).mockResolvedValue(false);

await projectCreator.setupSkills('/target/path', 'my-app');
await projectCreator.setupSkills('/target/path', 'my-app', {
bulmaFlavor: 'complete',
iconLibrary: 'none',
});

expect(fs.default.copy).not.toHaveBeenCalled();
expect(fs.default.writeFile).not.toHaveBeenCalled();
Expand Down
47 changes: 45 additions & 2 deletions create-bestax/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,54 @@ export const PROMPTS = {
} as const;

// Minimal CLAUDE.md scaffolded alongside the skills so an AI agent knows the
// stack and where the skills live.
export const CLAUDE_MD = (projectName: string): string => `# ${projectName}
// stack, this app's scaffold choices, and where the skills live. Kept short:
// it loads into every agent session.
export interface ClaudeMdOptions {
bulmaFlavor: string;
iconLibrary: string;
}

export const CLAUDE_MD = (
projectName: string,
{ bulmaFlavor, iconLibrary }: ClaudeMdOptions
): string => {
const flavor = BULMA_FLAVORS.find(f => f.name === bulmaFlavor);
const icon = ICON_LIBRARIES.find(lib => lib.name === iconLibrary);
const setupLines = [
`- Bulma flavor: **${bulmaFlavor}** — the app imports **prebuilt** CSS` +
(flavor ? ` (\`${flavor.importStatement.trim()}\`)` : '') +
`; there is no Sass pipeline unless you add \`sass\`.`,
];
if (flavor?.needsPrefix) {
setupLines.push(
`- Every Bulma class carries the \`bestax-\` prefix and the app is wrapped in ` +
`\`<ConfigProvider classPrefix="bestax-">\` — custom CSS selectors must match the prefix.`
);
}
setupLines.push(
iconLibrary === 'none'
? `- No icon library is installed — add one before using \`<Icon>\` ` +
`(https://bestax.io/docs/api/elements/icon).`
: `- Icon library: **${icon?.display ?? iconLibrary}** — use \`<Icon name="..." />\`.`
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return `# ${projectName}

This app is built with [\`@allxsmith/bestax-bulma\`](https://bestax.io) — React components for
Bulma 1.x.

## This app's setup

${setupLines.join('\n')}

## House style

- Never inline \`style={{}}\` — use the helper props every component accepts (\`m*\`/\`p*\`
spacing, \`textColor\`/\`bgColor\`, \`display="flex"\`, \`flexDirection\`, \`alignItems\`).
There is no \`gap\` helper — space children with margins.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blanket "space children with margins" overreaches past the flex context — 🟡 Minor · API

What: The bullet lists flexbox helpers, then states There is no gap helper — space children with margins. That is accurate for the flex/generic helper props (useFlexboxClasses/useBulmaClasses emit no gap class), but read as blanket advice it contradicts the library's own grid API: Grid accepts gap, columnGap, and rowGap props (bulma-ui/src/grid/Grid.tsx), and Columns has its own gap controls.

Why it matters: This file ships as the authoritative CLAUDE.md loaded into every agent session in the scaffolded app, and one of the bundled skills (bestax-layout-scaffold) explicitly builds card grids. An agent told to "space children with margins" may hand-roll margins on a Grid/Columns layout instead of the idiomatic gap prop — the exact custom-styling the house-style section is trying to prevent.

Fix: scope the sentence to flex, so it no longer reads as universal.

Suggested change
There is no \`gap\` helper — space children with margins.
There is no flexbox `gap` helper — space flex children with margins (for
grid/column layouts use `Grid`'s `gap`/`columnGap`/`rowGap` or `Columns`).

- Compose existing components before writing custom CSS; theme via \`Theme\` and \`--bulma-*\`
variables, never hardcoded colors.
- There is no test runner or Storybook in this app — don't assume one.

## AI skills

\`.claude/skills/\` contains Agent Skills that teach Claude how to build with this library. They load
Expand All @@ -68,6 +110,7 @@ Prefer the library's components and these skills over hand-written Bulma markup
- LLM-ready docs: https://bestax.io/llms.txt (curated index) and https://bestax.io/llms-full.txt
- Using bestax with AI tools: https://bestax.io/docs/guides/llms
`;
};

export interface IconLibrary {
name: string;
Expand Down
14 changes: 11 additions & 3 deletions create-bestax/src/project-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ICON_LIBRARIES,
BULMA_FLAVORS,
CLAUDE_MD,
type ClaudeMdOptions,
} from './constants.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -101,7 +102,11 @@ export class ProjectCreator {
await copyDirectory(templatePath, targetPath);
}

async setupSkills(targetPath: string, projectName: string): Promise<void> {
async setupSkills(
targetPath: string,
projectName: string,
options: ClaudeMdOptions
): Promise<void> {
const skillsSrc = path.join(this.templatesDir, 'skills');
if (!(await checkDirectoryExists(skillsSrc))) {
console.log(
Expand All @@ -113,7 +118,7 @@ export class ProjectCreator {
await fs.copy(skillsSrc, path.join(targetPath, '.claude', 'skills'));
await fs.writeFile(
path.join(targetPath, 'CLAUDE.md'),
CLAUDE_MD(projectName)
CLAUDE_MD(projectName, options)
);
console.log(chalk.green(MESSAGES.SKILLS_ADDED));
}
Expand Down Expand Up @@ -557,7 +562,10 @@ export class ProjectCreator {
template
);
if (installSkills) {
await this.setupSkills(targetPath, projectName);
await this.setupSkills(targetPath, projectName, {
bulmaFlavor,
iconLibrary,
});
}
displaySuccess(targetDir);
} catch (error) {
Expand Down
Loading