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
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
"generate:all": "pnpm --filter @thumbcode/dev-tools run generate:all"
},
"dependencies": {
"@thumbcode/config": "workspace:*",
"@thumbcode/core": "workspace:*",
"@thumbcode/state": "workspace:*",
"@thumbcode/types": "workspace:*",
"@thumbcode/ui": "workspace:*",
"@anthropic-ai/sdk": "^0.32.0",
"@react-native-async-storage/async-storage": "^2.0.0",
"@react-navigation/native": "^7.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ChatInput = () => {
const handleSend = () => {
if (text.trim()) {
addMessage({
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
text,
sender: 'user',
timestamp: new Date(),
Expand Down
20 changes: 20 additions & 0 deletions packages/config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@thumbcode/config",
"version": "1.0.0",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./env": "./src/env.ts",
"./constants": "./src/constants.ts",
"./features": "./src/features.ts"
},
"dependencies": {
"expo-constants": "~17.0.0"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-native": ">=0.70.0"
}
}
141 changes: 141 additions & 0 deletions packages/config/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Application Constants
*
* Centralized constants for the ThumbCode application.
*/

/**
* API base URLs
*/
export const API_URLS = {
github: 'https://api.github.com',
githubAuth: 'https://github.com',
anthropic: 'https://api.anthropic.com',
openai: 'https://api.openai.com/v1',
} as const;

/**
* GitHub OAuth configuration
*/
export const GITHUB_OAUTH = {
deviceCodeUrl: 'https://github.com/login/device/code',
accessTokenUrl: 'https://github.com/login/oauth/access_token',
verificationUri: 'https://github.com/login/device',
scopes: 'repo,user,read:org',
pollInterval: 5000, // 5 seconds
maxPollAttempts: 60, // 5 minutes max
} as const;

/**
* Secure store keys
*/
export const SECURE_STORE_KEYS = {
github: 'thumbcode_cred_github',
anthropic: 'thumbcode_cred_anthropic',
openai: 'thumbcode_cred_openai',
gitlab: 'thumbcode_cred_gitlab',
bitbucket: 'thumbcode_cred_bitbucket',
mcpServer: 'thumbcode_cred_mcp',
} as const;

/**
* Async storage keys
*/
export const STORAGE_KEYS = {
userSettings: 'thumbcode-user-settings',
credentials: 'thumbcode-credentials',
projects: 'thumbcode-projects',
agents: 'thumbcode-agents',
chat: 'thumbcode-chat',
onboarding: 'thumbcode-onboarding',
} as const;

/**
* Git configuration
*/
export const GIT_CONFIG = {
defaultBranch: 'main',
repoBaseDir: 'repos',
worktreeBaseDir: 'worktrees',
defaultDepth: 1, // Shallow clone by default
fetchInterval: 300000, // 5 minutes
} as const;

/**
* Agent configuration
*/
export const AGENT_CONFIG = {
maxConcurrent: 4,
defaultModel: 'claude-sonnet-4-20250514',
defaultTemperature: 0.7,
defaultMaxTokens: 4096,
taskTimeout: 300000, // 5 minutes
} as const;

/**
* UI configuration
*/
export const UI_CONFIG = {
animationDuration: 200,
toastDuration: 3000,
debounceDelay: 300,
maxFileTreeDepth: 10,
maxDiffLines: 1000,
} as const;

/**
* Rate limiting
*/
export const RATE_LIMITS = {
github: {
core: 5000, // requests per hour
search: 30, // requests per minute
},
anthropic: {
requestsPerMinute: 60,
tokensPerMinute: 100000,
},
openai: {
requestsPerMinute: 60,
tokensPerMinute: 150000,
},
} as const;

/**
* File size limits
*/
export const FILE_LIMITS = {
maxFileSizeBytes: 10 * 1024 * 1024, // 10MB
maxDiffSizeBytes: 1 * 1024 * 1024, // 1MB
maxContextFiles: 20,
maxCloneDepth: 10,
} as const;

/**
* Supported languages for syntax highlighting
*/
export const SUPPORTED_LANGUAGES = [
'javascript',
'typescript',
'python',
'rust',
'go',
'java',
'kotlin',
'swift',
'c',
'cpp',
'csharp',
'php',
'ruby',
'shell',
'sql',
'json',
'yaml',
'markdown',
'html',
'css',
'scss',
] as const;

export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
169 changes: 169 additions & 0 deletions packages/config/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* Environment Configuration Module
*
* Provides type-safe access to environment variables with validation.
* Uses expo-constants to access runtime configuration from app.config.ts.
*
* Usage:
* ```typescript
* import { env, validateEnvironment } from '@thumbcode/config/env';
*
* // Access environment variables
* console.log(env.appEnv); // 'development' | 'staging' | 'production'
*
* // Validate at app startup
* const result = validateEnvironment();
* if (!result.isValid) {
* console.error('Missing env vars:', result.missing);
* }
* ```
*/

import Constants from 'expo-constants';

/**
* App environment type
*/
export type AppEnvironment = 'development' | 'staging' | 'production';

/**
* Environment configuration interface
*/
export interface EnvironmentConfig {
/** Current app environment */
appEnv: AppEnvironment;

/** Whether development tools are enabled */
enableDevTools: boolean;

/** GitHub OAuth client ID for Device Flow */
githubClientId: string;

/** Expo project ID */
easProjectId: string;

/** Expo owner (account name) */
easOwner: string;

/** Whether we're running in development mode */
isDev: boolean;

/** Whether we're running in staging mode */
isStaging: boolean;

/** Whether we're running in production mode */
isProd: boolean;

/** App version */
version: string;

/** Build number */
buildNumber: string;
}

/**
* Required environment variables for each environment
*/
const REQUIRED_VARS: Record<AppEnvironment, (keyof EnvironmentConfig)[]> = {
development: [], // No strict requirements in dev
staging: ['githubClientId'],
production: ['githubClientId', 'easProjectId'],
};

/**
* Get the current app environment from Expo Constants
*/
function getAppEnv(): AppEnvironment {
const extra = Constants.expoConfig?.extra;
const appEnv = extra?.appEnv;

if (appEnv === 'staging' || appEnv === 'production') {
return appEnv;
}

return 'development';
}

/**
* Create the environment configuration object
*/
function createEnvConfig(): EnvironmentConfig {
const extra = Constants.expoConfig?.extra || {};
const appEnv = getAppEnv();

return {
appEnv,
enableDevTools: extra.enableDevTools ?? appEnv !== 'production',
githubClientId: extra.githubClientId || '',
easProjectId: extra.eas?.projectId || '',
easOwner: extra.eas?.owner || 'thumbcode',
isDev: appEnv === 'development',
isStaging: appEnv === 'staging',
isProd: appEnv === 'production',
version: Constants.expoConfig?.version || '0.0.0',
buildNumber: Constants.expoConfig?.ios?.buildNumber ||
Constants.expoConfig?.android?.versionCode?.toString() ||
'1',
};
}

/**
* Environment configuration singleton
*/
export const env = createEnvConfig();

/**
* Validation result for environment check
*/
export interface ValidationResult {
isValid: boolean;
missing: string[];
warnings: string[];
}

/**
* Validate that all required environment variables are set
*
* @returns Validation result with missing variables
*/
export function validateEnvironment(): ValidationResult {
const missing: string[] = [];
const warnings: string[] = [];

const requiredVars = REQUIRED_VARS[env.appEnv];

for (const varName of requiredVars) {
const value = env[varName];
if (!value || (typeof value === 'string' && value.trim() === '')) {
missing.push(varName);
}
}

// Add warnings for common issues
if (env.isProd && !env.easProjectId) {
warnings.push('EXPO_PROJECT_ID is not set - EAS builds may fail');
}

if (!env.githubClientId) {
warnings.push('EXPO_PUBLIC_GITHUB_CLIENT_ID is not set - GitHub auth will be disabled');
}

// Log warnings in development
if (env.isDev) {
for (const warning of warnings) {
console.warn(`[ENV] ${warning}`);
}

if (missing.length > 0) {
console.warn(`[ENV] Missing required variables: ${missing.join(', ')}`);
}
}

return {
isValid: missing.length === 0,
missing,
warnings,
};
}

export default env;
Loading