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
47 changes: 47 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"permissions": {
"allow": [
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git worktree:*)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "bun run typecheck 2>&1 | head -30"
},
{
"type": "command",
"command": "bun run lint:fix 2>&1 | head -30"
},
{
"type": "command",
"command": "bun run knip:fix 2>&1 | head -50"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bun run typecheck 2>&1 | head -30"
},
{
"type": "command",
"command": "bun run lint:fix 2>&1 | head -30"
},
{
"type": "command",
"command": "bun run knip:fix 2>&1 | head -50"
}
]
}
]
}
}
26 changes: 26 additions & 0 deletions .github/workflows/knip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Knip

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
knip:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run Knip
run: bun run knip
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ coverage/
*.seed
*.pid.lock

.worktrees
87 changes: 75 additions & 12 deletions bun.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions knip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/cli/index.ts", "tests/**/testkit/index.ts"],
"project": ["src/**/*.ts", "tests/**/*.ts"],
"ignore": ["dist/**", "tests/fixtures/**"]
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"lint": "biome check src tests",
"lint:fix": "biome check --write src tests",
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"knip": "knip",
"knip:fix": "knip --fix"
},
"keywords": [
"base44",
Expand All @@ -44,7 +46,6 @@
"@types/json-schema": "^7.0.15",
"@types/lodash.kebabcase": "^4.1.9",
"@types/node": "^22.10.5",
"@types/tar": "^6.1.13",
"@vercel/detect-agent": "^1.1.0",
"chalk": "^5.6.2",
"commander": "^12.1.0",
Expand All @@ -59,9 +60,10 @@
"http-proxy-middleware": "^3.0.5",
"json-schema-to-typescript": "^15.0.4",
"json5": "^2.2.3",
"knip": "^5.83.1",
"ky": "^1.14.2",
"lodash.kebabcase": "^4.1.1",
"msw": "^2.12.7",
"msw": "^2.12.10",
"nanoid": "^5.1.6",
"open": "^11.0.0",
"p-wait-for": "^6.0.0",
Expand Down
1 change: 0 additions & 1 deletion src/cli/telemetry/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ export const POSTHOG_API_KEY =
"phc_VsHW5HxTzpORanESQh9A08tmZLQkKbtIBTYoQvRpPOp";
export const TELEMETRY_DISABLED_ENV_VAR = "BASE44_DISABLE_TELEMETRY";
export const POSTHOG_REQUEST_TIMEOUT_MS = 1000;
export const POSTHOG_SHUTDOWN_TIMEOUT_MS = 1000;
7 changes: 0 additions & 7 deletions src/cli/telemetry/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1 @@
export { addCommandInfoToErrorReporter } from "./commander-hooks.js";
export type { ErrorContext } from "./error-reporter.js";
export { ErrorReporter } from "./error-reporter.js";
export {
getPostHogClient,
isTelemetryEnabled,
shutdownPostHog,
} from "./posthog.js";
16 changes: 0 additions & 16 deletions src/cli/telemetry/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PostHog } from "posthog-node";
import {
POSTHOG_API_KEY,
POSTHOG_REQUEST_TIMEOUT_MS,
POSTHOG_SHUTDOWN_TIMEOUT_MS,
TELEMETRY_DISABLED_ENV_VAR,
} from "./consts.js";

Expand Down Expand Up @@ -43,18 +42,3 @@ export function getPostHogClient(): PostHog | null {

return client;
}

export async function shutdownPostHog(): Promise<void> {
if (!client) {
return;
}

const clientToShutdown = client;
client = null; // Prevent further use

try {
await clientToShutdown.shutdown(POSTHOG_SHUTDOWN_TIMEOUT_MS);
} catch {
// Silent - don't let shutdown errors block CLI exit
}
}
2 changes: 1 addition & 1 deletion src/cli/utils/runCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isLoggedIn, readAuth } from "@/core/auth/index.js";
import { isCLIError } from "@/core/errors.js";
import { initAppConfig } from "@/core/project/index.js";

export interface RunCommandOptions {
interface RunCommandOptions {
/**
* Use the full ASCII art banner instead of the simple intro tag.
* Useful for commands like `create` that want more visual impact.
Expand Down
23 changes: 1 addition & 22 deletions src/core/auth/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import { renewAccessToken } from "@/core/auth/api.js";
import type { AuthData } from "@/core/auth/schema.js";
import { AuthDataSchema } from "@/core/auth/schema.js";
import { getAuthFilePath } from "@/core/config.js";
import {
AuthRequiredError,
FileReadError,
SchemaValidationError,
} from "@/core/errors.js";
import { FileReadError, SchemaValidationError } from "@/core/errors.js";
import { deleteFile, readJsonFile, writeJsonFile } from "@/core/utils/fs.js";

// Buffer time before expiration to trigger proactive refresh (60 seconds)
Expand Down Expand Up @@ -143,20 +139,3 @@ export async function isLoggedIn(): Promise<boolean> {
return false;
}
}

/**
* Ensures the user is logged in before proceeding.
*
* @throws {AuthRequiredError} If the user is not logged in.
*
* @example
* await requireAuth();
* // Code here will only run if user is authenticated
*/
export async function requireAuth(): Promise<void> {
if (!(await isLoggedIn())) {
throw new AuthRequiredError(
"Not logged in. Please run 'base44 login' first.",
);
}
}
2 changes: 0 additions & 2 deletions src/core/auth/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ export const OAuthErrorSchema = z.object({
error_description: z.string().optional(),
});

export type OAuthError = z.infer<typeof OAuthErrorSchema>;

export const UserInfoSchema = z.object({
email: z.email(),
name: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
// Templates are copied to dist/templates/
const __dirname = dirname(fileURLToPath(import.meta.url));

export function getBase44GlobalDir(): string {
function getBase44GlobalDir(): string {
return join(homedir(), ".base44");
}

Expand Down
10 changes: 5 additions & 5 deletions src/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface ErrorHint {
command?: string; // Optional command to run
}

export interface CLIErrorOptions {
interface CLIErrorOptions {
hints?: ErrorHint[];
cause?: Error;
}
Expand All @@ -72,7 +72,7 @@ export interface CLIErrorOptions {
* Base class for all CLI errors.
* Provides structured error data with code, hints, and cause tracking.
*/
export abstract class CLIError extends Error {
abstract class CLIError extends Error {
abstract readonly code: string;
readonly hints: ErrorHint[];
override readonly cause?: Error;
Expand All @@ -92,13 +92,13 @@ export abstract class CLIError extends Error {
* User errors - the user did something wrong that they can fix.
* Examples: not logged in, invalid config, missing project
*/
export abstract class UserError extends CLIError {}
abstract class UserError extends CLIError {}

/**
* System errors - something broke that needs investigation.
* Examples: API failures, network issues, file system errors
*/
export abstract class SystemError extends CLIError {}
abstract class SystemError extends CLIError {}

// ============================================================================
// User Errors
Expand Down Expand Up @@ -248,7 +248,7 @@ export class InvalidInputError extends UserError {
// System Errors
// ============================================================================

export interface ApiErrorOptions extends CLIErrorOptions {
interface ApiErrorOptions extends CLIErrorOptions {
statusCode?: number;
requestUrl?: string;
requestMethod?: string;
Expand Down
8 changes: 3 additions & 5 deletions src/core/project/app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { AppConfig } from "@/core/project/schema.js";
import { AppConfigSchema } from "@/core/project/schema.js";
import { readJsonFile, writeFile } from "@/core/utils/fs.js";

export interface CachedAppConfig {
interface CachedAppConfig {
id: string;
projectRoot: string;
}
Expand Down Expand Up @@ -89,7 +89,7 @@ export function setAppConfig(config: CachedAppConfig): void {
cache = config;
}

export function generateAppConfigContent(id: string): string {
function generateAppConfigContent(id: string): string {
return `// Base44 App Configuration
// This file links your local project to your Base44 app.
// Do not commit this file to version control.
Expand All @@ -109,9 +109,7 @@ export async function writeAppConfig(
return configPath;
}

export async function findAppConfigPath(
projectRoot: string,
): Promise<string | null> {
async function findAppConfigPath(projectRoot: string): Promise<string | null> {
const files = await globby(APP_CONFIG_PATTERN, {
cwd: projectRoot,
absolute: true,
Expand Down
4 changes: 2 additions & 2 deletions src/core/project/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { createProject, downloadProject } from "@/core/project/api.js";
import type { Template } from "@/core/project/schema.js";
import { renderTemplate } from "@/core/project/template.js";

export interface CreateProjectOptions {
interface CreateProjectOptions {
name: string;
description?: string;
path: string;
template: Template;
}

export interface CreateProjectResult {
interface CreateProjectResult {
projectId: string;
projectDir: string;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/project/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function hasResourcesToDeploy(projectData: ProjectData): boolean {
/**
* Result of deploying all project resources.
*/
export interface DeployAllResult {
interface DeployAllResult {
/**
* The app URL if a site was deployed, undefined otherwise.
*/
Expand Down
5 changes: 0 additions & 5 deletions src/core/project/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export const TemplatesConfigSchema = z.object({
});

export type Template = z.infer<typeof TemplateSchema>;
export type TemplatesConfig = z.infer<typeof TemplatesConfigSchema>;

const SiteConfigSchema = z.object({
buildCommand: z.string().optional(),
serveCommand: z.string().optional(),
Expand All @@ -34,7 +32,6 @@ export const ProjectConfigSchema = z.object({
agentsDir: z.string().optional().default("agents"),
});

export type SiteConfig = z.infer<typeof SiteConfigSchema>;
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;

export const AppConfigSchema = z.object({
Expand All @@ -47,8 +44,6 @@ export const CreateProjectResponseSchema = z.looseObject({
id: z.string(),
});

export type CreateProjectResponse = z.infer<typeof CreateProjectResponseSchema>;

export const ProjectSchema = z
.object({
id: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion src/core/project/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Template } from "@/core/project/schema.js";
import { TemplatesConfigSchema } from "@/core/project/schema.js";
import { copyFile, readJsonFile, writeFile } from "@/core/utils/fs.js";

export interface TemplateData {
interface TemplateData {
name: string;
description?: string;
projectId: string;
Expand Down
19 changes: 0 additions & 19 deletions src/core/resources/agent/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,6 @@ import {
import type { AgentConfig, AgentConfigApiResponse } from "./schema.js";
import { AgentConfigSchema } from "./schema.js";

export function generateAgentConfigContent(name: string): string {
return `// Base44 Agent Configuration
// Agent name must be lowercase alphanumeric with underscores only
{
"name": "${name}",
// Brief description of what this agent does
"description": "",
// Detailed instructions for the agent's behavior
"instructions": "",
// Tool configurations - entity tools and backend function tools
// Entity tool example: { "entity_name": "tasks", "allowed_operations": ["read", "create"] }
// Function tool example: { "function_name": "send_email", "description": "Send an email" }
"tool_configs": [],
// Optional WhatsApp greeting message
"whatsapp_greeting": null
}
`;
}

async function readAgentFile(agentPath: string): Promise<AgentConfig> {
const parsed = await readJsonFile(agentPath);
const result = AgentConfigSchema.safeParse(parsed);
Expand Down
Loading
Loading