Skip to content

Conversation

@pitzcarraldo
Copy link
Contributor

@pitzcarraldo pitzcarraldo commented Jan 21, 2026

Summary

Add comprehensive Firebase configuration detection and validation system. Users can now automatically detect Firebase credential files (google-services.json for Android, GoogleService-Info.plist for iOS), validate them against Firebase schemas, and optionally download configs directly from Firebase Console using OAuth 2.0 authentication.

Details

Core Components:

  • detector.ts: Automatically detects Firebase credential files and their locations
  • validator.ts: Validates credentials against Zod schemas with detailed error messages
  • firebase-service.ts: Main service orchestrating detection, validation, and recommendations
  • FirebaseWizard.tsx: Interactive UI for configuration management
  • OAuth 2.0 authentication with PKCE support for Firebase Management API access

Key Features:

  • Supports React Native, Flutter, iOS, and Android platforms
  • Handles both JSON and XML plist formats for iOS configuration
  • Cross-platform project ID verification
  • Detailed issue reporting with recommendations and help URLs
  • /firebase command for interactive configuration workflow

How to Validate

  1. Run bun run dev to start interactive mode
  2. Type /firebase to open the Firebase configuration wizard
  3. Test detection with sample Firebase config files
  4. Verify output for both valid and invalid configurations

Pre-Merge Checklist

Code Quality

  • Code builds without errors (bun run build)
  • Types check correctly (bun run typecheck)
  • Linter passes (bun run lint)
  • Tests pass (bun test - 529 tests)

Documentation

  • Updated llms.txt with command documentation
  • Updated README.md with /firebase command
  • Updated SKILL.md files for doctor and install

Commit Standards

  • Follows Conventional Commits format
  • No breaking changes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added /firebase interactive command and CLI wizard for checking & configuring Firebase credentials.
    • OAuth-based authentication to list projects/apps and download Android/iOS config files.
    • Automatic detection and cross-platform validation of Android (google-services.json) and iOS (GoogleService-Info.plist).
    • New CLI status display showing detailed credential validity and issues.
  • Documentation

    • README, installation guides, and diagnostics updated with Firebase verification steps and usage notes.

✏️ Tip: You can customize this high-level summary in your review settings.

- Detect Firebase credential files (google-services.json, GoogleService-Info.plist)
- Support XML and JSON plist formats for iOS configuration
- Validate credentials against Zod schemas
- Interactive wizard for Firebase configuration management
- OAuth 2.0 with PKCE support for downloading configs from Firebase Console
- Support React Native, Flutter, iOS, and Android platforms

Key features:
- Automatic detection of platform and expected credential file locations
- Comprehensive validation with detailed error messages
- Project ID verification across platforms
- Integration with existing Clix CLI command system

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a complete Firebase configuration feature set: CLI command and Ink UI for an interactive Firebase wizard, OAuth-based authentication, Firebase Management API client, credential detection/validation for Android and iOS, download utilities, error types, schemas/validators, and documentation updates.

Changes

Cohort / File(s) Summary
Docs & Metadata
README.md, llms.txt, package.json
Documented new /firebase slash command; updated llms help text and counts; added runtime deps google-auth-library, plist and dev @types/plist.
CLI Command & Registry
src/lib/commands/firebase.tsx, src/lib/commands/registry.ts
New /firebase Command exposing a FirebaseWizard UI and registered in built-in commands.
Errors
src/lib/errors/types.ts
Added Firebase-specific error codes and exported FirebaseError class with platform and optional file context.
API Client & Types
src/lib/services/firebase/api/firebase-api.ts, src/lib/services/firebase/api/types.ts, src/lib/services/firebase/api/index.ts
New Firebase Management API client (paginated project/app listing, config retrieval) plus typed response interfaces and re-exports.
Credential Detection
src/lib/services/firebase/detector.ts
Platform detection and discovery of google-services.json / GoogleService-Info.plist (expected and misplaced locations), parsing, validation hooks, and structured detection results.
Schemas & Validation
src/lib/services/firebase/schemas.ts, src/lib/services/firebase/validator.ts
Zod schemas (full/minimal) for Android/iOS config files and validators for format, package/bundle matches, and project ID consistency; extraction helpers.
OAuth & Token Storage
src/lib/services/firebase/oauth/*
PKCE-based Google OAuth client, config/constants, token types, XDG-backed TokenStore, and index barrel for auth utilities.
Downloader Service
src/lib/services/firebase/downloader.ts
OAuth-backed downloader to list projects/apps, locate apps by package/bundle, download platform config files, and determine expected save paths.
Firebase Core Service & Barrel
src/lib/services/firebase/firebase-service.ts, src/lib/services/firebase/index.ts
FirebaseService for detection, status, recommendations, and helpers; central export barrel re-exporting API, detector, downloader, oauth, schemas, types, and validator.
Types & Public Models
src/lib/services/firebase/types.ts
Added platform/credential/file/issue/status types, detection/result interfaces, help URL constants, and wizard/action types.
Embedded Skills & Diagnostics
src/lib/embedded-skills.ts, src/lib/skills/doctor/SKILL.md, src/lib/skills/install/SKILL.md
Added verification step and expanded diagnostic output & docs; new diagnostic fields: firebaseAndroid, firebaseIos, firebasePackageMatch, firebaseBundleMatch; referenced /firebase command.
UI Components
src/ui/components/FirebaseStatusDisplay.tsx, src/ui/components/FirebaseWizard.tsx
New Ink-based FirebaseStatusDisplay component and comprehensive multi-phase FirebaseWizard interactive UI (authenticate, select project/app, download, validate, complete).
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding Firebase configuration detection and validation, which is the primary focus of this comprehensive changeset.
Description check ✅ Passed The PR description provides a complete summary, detailed core components, key features, clear validation steps, and a comprehensive pre-merge checklist with all items marked as completed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0c56059c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +302 to +306
// Also search for app-specific plist locations in iOS directory
const iosDir = path.join(projectPath, 'ios');
try {
const entries = await fs.readdir(iosDir, { withFileTypes: true });
for (const entry of entries) {

Choose a reason for hiding this comment

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

P2 Badge Expand iOS plist search beyond ios/ subdir

The iOS detector only looks in IOS_SEARCH_PATHS and then scans projectPath/ios/*/GoogleService-Info.plist. Native iOS projects commonly store the file inside the app target directory at the repo root (e.g., MyApp/GoogleService-Info.plist next to the .xcodeproj), so this function will report “not found” even when a valid plist exists. That means /firebase will incorrectly flag missing credentials and recommend a download for standard native iOS layouts. Consider also searching top-level app directories (e.g., siblings of .xcodeproj) or a broader root-level glob for */GoogleService-Info.plist when the detected platform is ios.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Fix all issues with AI agents
In `@llms.txt`:
- Around line 229-237: The documentation added a new system command `/firebase`,
increasing the system commands from 9 to 10, so update the earlier slash-command
totals to read "3 autonomous + 5 skills + 10 system = 18 total" (or update any
"total slash-commands" summary) to avoid inconsistency; locate the earlier
totals/summary text that lists the counts (look for phrases like "autonomous",
"skills", "system", or "total slash-commands") and change the numbers and the
computed total to 18 wherever they appear.

In `@src/lib/services/firebase/detector.ts`:
- Around line 327-382: The detector currently treats any file flagged as
"inExpectedLocation" by findGoogleServicesJson but that helper marks expected
locations globally; update detectAndroidCredential to prefer and label files
using the platform-specific expectedPaths: when selecting expectedFile, find a
file whose path matches any entry in expectedPaths (compare normalized relative
paths or compare absolutePath to path.join(projectPath, expectedPath)), set
inExpectedLocation based on that match, and set expectedPath only when the
chosen file is not inExpectedLocation (use the first matching expectedPaths
value if applicable); apply the same change to the iOS detector
(detectIosCredential / findGoogleServiceInfoPlist) so validation and guidance
use the platform-specific expectedPaths.
- Around line 149-176: The function detectPlatform currently returns
'react-native' when detectNativePlatforms finds both hasIos and hasAndroid even
if isReactNativeProject/isFlutterProject were false; change the logic in
detectPlatform so that when hasIos && hasAndroid and neither
isReactNativeProject(projectPath) nor isFlutterProject(projectPath) are true, it
returns 'unknown' instead of 'react-native' to avoid misclassifying purely
native monorepos; update the hasIos && hasAndroid branch in detectPlatform (and
any related comments) to implement this conditional return and add/update tests
that assert dual-native-but-not-cross-platform repos yield 'unknown'.

In `@src/lib/services/firebase/oauth/auth-client.ts`:
- Around line 96-167: The waitForCallback Promise currently starts a server
bound to all interfaces and never clears the timeout; change server.listen(...)
to bind explicitly to '127.0.0.1' using GOOGLE_OAUTH_CONFIG.callbackPort, store
the setTimeout return value in a variable (e.g., timeoutHandle), and add a small
cleanup helper (e.g., cleanup = () => { clearTimeout(timeoutHandle); try {
server.close(); } catch {} }) that you call before every resolve and reject
(inside the OAuth success branch where resolve({ code, state }) is called, in
the error and missing-code branches before reject, in the server.on('error')
handler, and in the timeout callback) so the server is closed and the timeout
cleared on all code paths.
- Around line 34-89: generate and store a cryptographically-random OAuth state
string when building the URL in generateAuthUrl() (add a private this.state:
string | null), include that state in the client.generateAuthUrl(...)
parameters, and in waitForCallback() validate that the incoming state equals the
stored this.state before accepting the code; if missing or mismatched,
throw/reject and clear the stored state. Ensure state is cleared after
successful or failed validation to avoid reuse.

In `@src/lib/services/firebase/oauth/token-store.ts`:
- Around line 45-49: The save method writes OAuth tokens with default permissive
filesystem bits; change mkdir and file writes to enforce restrictive
permissions: create the directory with mode 0o700 (use fs.mkdir(..., {
recursive: true, mode: 0o700 })) and write the token file with mode 0o600 (use
fs.writeFile with the mode option or follow up with fs.chmod(this.tokenPath,
0o600)). Update the save function (in token-store.ts, method save) to ensure the
directory and file permissions are set atomically/explicitly so the token JSON
is only readable by the owner.

In `@src/lib/services/firebase/schemas.ts`:
- Around line 12-17: The AndroidClientInfoSchema currently allows uppercase
letters due to the case-insensitive regex flag; update the schema's regex in
AndroidClientInfoSchema by removing the `i` flag so the pattern becomes strictly
lowercase (use `/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/`) to enforce lowercase
package names while keeping the existing min(1) and error message intact.

In `@src/ui/components/FirebaseWizard.tsx`:
- Around line 828-833: In handleAction (the async callback handling
CredentialAction), await the async call to handleDownload so the promise is
awaited and potential rejections are propagated/handled; update the 'download'
case to use await handleDownload() (keeping the surrounding async handleAction)
and ensure any needed try/catch around the switch remains or is added to surface
errors from handleDownload.
- Around line 149-162: The openBrowser function is vulnerable to shell injection
and mishandles Windows arguments while ignoring exec errors; replace the
string-interpolated exec call with safe child_process APIs (e.g., spawn or
execFile) and pass the URL as an argument array instead of interpolating into a
shell command, handle Windows by invoking cmd /c start with an explicit empty
title (e.g., ['cmd', ['/c','start','', url]] or equivalent) or call
spawn('start', ['', url], {shell:true}) carefully, and attach an error
callback/listener to surface failures (or log/throw the error) so exec/spawn
errors are not silently ignored; update references in openBrowser and any call
sites accordingly.
🧹 Nitpick comments (13)
src/lib/services/firebase/oauth/types.ts (1)

12-18: Redundant field declarations in extended interface.

OAuthTokens extends Credentials from google-auth-library, which already declares access_token, refresh_token, token_type, and expiry_date. Re-declaring them here is redundant but documents the expected shape explicitly. Consider removing the duplicates or adding a comment explaining the intent.

src/lib/services/firebase/api/types.ts (1)

31-35: Consider adding 'STATE_UNSPECIFIED' to project state union.

The Firebase Management API can return STATE_UNSPECIFIED in addition to ACTIVE and DELETED. Adding it would make the type more robust against unexpected API responses.

Suggested change
   /**
    * Project state.
    */
-  state: 'ACTIVE' | 'DELETED';
+  state: 'STATE_UNSPECIFIED' | 'ACTIVE' | 'DELETED';
src/ui/components/FirebaseStatusDisplay.tsx (3)

99-109: Potential non-unique React key if errors have duplicate path+message.

The key ${error.path}-${error.message} could collide if the same error appears multiple times. Consider appending the index for guaranteed uniqueness.

Suggested fix
-              {credential.errors.slice(0, 3).map((error) => (
-                <Text key={`${error.path}-${error.message}`} color="red">
+              {credential.errors.slice(0, 3).map((error, index) => (
+                <Text key={`${error.path}-${error.message}-${index}`} color="red">

154-166: Consider extracting platform-needs helpers to a shared utility.

platformNeedsAndroid and platformNeedsIos duplicate logic from downloader.ts (lines 179-181). Extract to a shared utility in the firebase module to maintain consistency.


180-187: getStatusColor returns 'red' for both missing and n/a statuses.

Currently n/a and both render as red. Consider using a neutral color (e.g., dimColor or gray) for n/a to differentiate "not applicable" from "missing/error".

Suggested fix
 function getStatusColor(status: string): string {
   if (status === '✓') return 'green';
   if (status === '!') return 'yellow';
+  if (status === 'n/a') return 'gray';
   return 'red';
 }
src/lib/services/firebase/downloader.ts (1)

145-168: Consider adding error handling for file write operations.

downloadAndroidConfig and downloadIosConfig don't catch errors from fs.mkdir or fs.writeFile. If the directory creation or file write fails (e.g., permission denied), the error propagates uncaught. Consider wrapping in try-catch with meaningful error messages.

src/ui/components/FirebaseWizard.tsx (1)

656-706: Consider simplifying callback dependency management.

The ref-based pattern to avoid circular dependencies works but adds complexity. Consider extracting the download flow into a custom hook or using a reducer pattern for cleaner state management.

src/lib/services/firebase/firebase-service.ts (1)

64-71: Consider extracting platform requirements logic to a shared utility.

The needsAndroid/needsIos computation is duplicated across multiple files (here, in FirebaseWizard.tsx, and in detector.ts). Extract this to a shared helper function to ensure consistency.

♻️ Example shared utility
// In types.ts or a new utils.ts
export function getPlatformRequirements(platform: Platform): { needsAndroid: boolean; needsIos: boolean } {
  const needsAndroid = platform === 'android' || platform === 'react-native' || platform === 'flutter';
  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
  return { needsAndroid, needsIos };
}
src/lib/services/firebase/api/firebase-api.ts (2)

39-56: Consider adding request timeout.

The fetch calls have no timeout configuration. Long-running or hung requests could block the CLI indefinitely. Consider using AbortController with a timeout.

♻️ Example with timeout
 private async request<T>(path: string): Promise<T> {
   const token = await this.getAccessToken();
   const url = `${BASE_URL}${path}`;
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 30000);

-  const response = await fetch(url, {
-    headers: {
-      Authorization: `Bearer ${token}`,
-      'Content-Type': 'application/json',
-    },
-  });
+  try {
+    const response = await fetch(url, {
+      headers: {
+        Authorization: `Bearer ${token}`,
+        'Content-Type': 'application/json',
+      },
+      signal: controller.signal,
+    });

     if (!response.ok) {
       const error = await response.text();
       throw new Error(`Firebase API error (${response.status}): ${error}`);
     }

     return response.json() as Promise<T>;
+  } finally {
+    clearTimeout(timeoutId);
+  }
 }

103-106: Consider URL-encoding path parameters defensively.

While Firebase project IDs and app IDs typically don't contain special characters, encoding them with encodeURIComponent would be a defensive measure against edge cases.

src/lib/services/firebase/types.ts (2)

86-90: Naming conflict with existing ValidationError class.

This ValidationError interface shadows the ValidationError class in src/lib/errors/types.ts. Consider renaming to FirebaseValidationError or CredentialValidationError to avoid confusion when both are imported.


104-125: Consider documenting the valid/content invariant.

The content field is optional, but when valid is true, content should always be defined. This invariant is relied upon in firebase-service.ts. Consider adding a JSDoc comment to document this relationship.

📝 Documentation suggestion
 export interface FirebaseCredentialFile {
   /** File path relative to project root */
   path: string;
   /** Absolute file path */
   absolutePath: string;
   /** Target platform */
   platform: 'android' | 'ios';
   /** Credential file type */
   type: CredentialFileType;
   /** Whether file exists */
   exists: boolean;
   /** Whether file is valid */
   valid: boolean;
   /** Validation errors if any */
   errors: ValidationError[];
-  /** Parsed content if valid */
+  /** Parsed content if valid. Guaranteed to be defined when `valid` is true. */
   content?: GoogleServicesJson | GoogleServiceInfoPlist;
   /** Whether file is in expected location */
   inExpectedLocation: boolean;
   /** Expected location if different from actual */
   expectedPath?: string;
 }
src/lib/services/firebase/detector.ts (1)

607-623: Reuse the existing needsAndroidConfig/needsIosConfig helpers.

Avoids duplicating platform logic and keeps behavior consistent.

♻️ Suggested refactor
-  const needsAndroid =
-    platform === 'android' || platform === 'react-native' || platform === 'flutter';
-  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
+  const needsAndroid = needsAndroidConfig(platform);
+  const needsIos = needsIosConfig(platform);

Comment on lines 149 to 162
function openBrowser(url: string): void {
const platform = process.platform;

let command: string;
if (platform === 'darwin') {
command = `open "${url}"`;
} else if (platform === 'win32') {
command = `start "${url}"`;
} else {
command = `xdg-open "${url}"`;
}

exec(command);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Shell command injection risk and missing error handling in openBrowser.

The URL is interpolated directly into shell commands without proper escaping. On Windows, start interprets the first quoted argument as a window title, causing the URL to be mishandled. Additionally, exec errors are silently ignored.

🔒 Proposed fix
-function openBrowser(url: string): void {
-  const platform = process.platform;
-
-  let command: string;
-  if (platform === 'darwin') {
-    command = `open "${url}"`;
-  } else if (platform === 'win32') {
-    command = `start "${url}"`;
-  } else {
-    command = `xdg-open "${url}"`;
-  }
-
-  exec(command);
-}
+function openBrowser(url: string): void {
+  const platform = process.platform;
+
+  let command: string;
+  let args: string[];
+  if (platform === 'darwin') {
+    command = 'open';
+    args = [url];
+  } else if (platform === 'win32') {
+    command = 'cmd';
+    args = ['/c', 'start', '""', url];
+  } else {
+    command = 'xdg-open';
+    args = [url];
+  }
+
+  const { spawn } = require('node:child_process');
+  spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function openBrowser(url: string): void {
const platform = process.platform;
let command: string;
if (platform === 'darwin') {
command = `open "${url}"`;
} else if (platform === 'win32') {
command = `start "${url}"`;
} else {
command = `xdg-open "${url}"`;
}
exec(command);
}
function openBrowser(url: string): void {
const platform = process.platform;
let command: string;
let args: string[];
if (platform === 'darwin') {
command = 'open';
args = [url];
} else if (platform === 'win32') {
command = 'cmd';
args = ['/c', 'start', '""', url];
} else {
command = 'xdg-open';
args = [url];
}
const { spawn } = require('node:child_process');
spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
}
🤖 Prompt for AI Agents
In `@src/ui/components/FirebaseWizard.tsx` around lines 149 - 162, The openBrowser
function is vulnerable to shell injection and mishandles Windows arguments while
ignoring exec errors; replace the string-interpolated exec call with safe
child_process APIs (e.g., spawn or execFile) and pass the URL as an argument
array instead of interpolating into a shell command, handle Windows by invoking
cmd /c start with an explicit empty title (e.g., ['cmd', ['/c','start','', url]]
or equivalent) or call spawn('start', ['', url], {shell:true}) carefully, and
attach an error callback/listener to surface failures (or log/throw the error)
so exec/spawn errors are not silently ignored; update references in openBrowser
and any call sites accordingly.

- Add CSRF protection via OAuth state parameter validation
- Bind callback server to localhost only for security
- Add proper timeout cleanup in OAuth flow
- Harden token file permissions (0o600 for file, 0o700 for dir)
- Use spawn instead of exec for shell injection prevention
- Add missing await for handleDownload
- Return 'unknown' for dual-platform detection
- Expand iOS plist search to include root-level directories
- Use platform-specific expectedPaths for credential detection
- Enforce lowercase for Android package names in schema
- Update llms.txt slash-command count (17→18)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/lib/services/firebase/detector.ts`:
- Around line 632-648: detectFirebaseConfig currently treats an 'unknown'
platform as not needing Android or iOS, causing configured to be true when both
configs are missing; update detectFirebaseConfig to call
detectNativePlatforms(projectPath) when detectPlatform returns 'unknown' and use
its result to set needsAndroid and needsIos (instead of defaulting to false), so
the boolean flags reflect actual native platform presence before computing
androidConfigured, iosConfigured and configured; reference detectFirebaseConfig,
detectPlatform, detectNativePlatforms, needsAndroid, needsIos and
getExpectedPaths when making the change.
♻️ Duplicate comments (2)
src/lib/services/firebase/oauth/token-store.ts (1)

45-53: Ensure existing token files are re-permissioned too.

fs.writeFile(..., { mode }) only sets permissions on newly created files; if a token file already exists from an older version, it can keep permissive bits. Consider chmod after write (and optionally on the directory) to guarantee owner-only access on upgrades.

🔐 Suggested hardening
   await fs.mkdir(dir, { recursive: true, mode: 0o700 });
   // Write token file with restricted permissions (owner read/write only)
   await fs.writeFile(this.tokenPath, JSON.stringify(tokens, null, 2), {
     encoding: 'utf-8',
     mode: 0o600,
   });
+  // Ensure existing paths are tightened as well
+  await fs.chmod(dir, 0o700);
+  await fs.chmod(this.tokenPath, 0o600);
src/ui/components/FirebaseWizard.tsx (1)

150-169: Handle spawn errors to avoid CLI crashes when browser open fails.

If open/xdg-open/cmd isn’t available, spawn emits an error event; without a handler, Node can throw. Add an error listener (and ideally surface a user-facing message).

🛠 Minimal safeguard
-  spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
+  const child = spawn(command, args, { detached: true, stdio: 'ignore' });
+  child.on('error', (err) => {
+    // eslint-disable-next-line no-console
+    console.error('Failed to open browser:', err.message);
+  });
+  child.unref();

Comment on lines +632 to +648
export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> {
const platform = await detectPlatform(projectPath);
const expectedPaths = getExpectedPaths(platform, projectPath);

const android = await detectAndroidCredential(projectPath, expectedPaths.android);
const ios = await detectIosCredential(projectPath, expectedPaths.ios);

const issues = generateIssues(android, ios, platform, expectedPaths);

// Determine if Firebase is configured
const needsAndroid =
platform === 'android' || platform === 'react-native' || platform === 'flutter';
const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';

const androidConfigured = !needsAndroid || (android?.valid ?? false);
const iosConfigured = !needsIos || (ios?.valid ?? false);
const configured = androidConfigured && iosConfigured;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n src/lib/services/firebase/detector.ts | head -100

Repository: clix-so/clix-cli

Length of output: 3392


🏁 Script executed:

wc -l src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 101


🏁 Script executed:

sed -n '620,660p' src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 1306


🏁 Script executed:

# Search for detectPlatform implementation
rg -A 10 "function detectPlatform|const detectPlatform" src/lib/services/firebase/

Repository: clix-so/clix-cli

Length of output: 778


🏁 Script executed:

# Search for detectNativePlatforms to see if it exists
rg "detectNativePlatforms" .

Repository: clix-so/clix-cli

Length of output: 254


🏁 Script executed:

# Look at the platform detection logic and how 'unknown' is returned
rg -B 5 -A 5 "unknown" src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 1415


🏁 Script executed:

sed -n '110,160p' src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 1325


🏁 Script executed:

# Get the full detectNativePlatforms function
rg -A 30 "async function detectNativePlatforms" src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 886


🏁 Script executed:

# Get full detectPlatform function to see how detectNativePlatforms is used
rg -A 50 "export async function detectPlatform" src/lib/services/firebase/detector.ts

Repository: clix-so/clix-cli

Length of output: 1469


unknown platform incorrectly reports configured: true when Firebase configs are missing.

For dual-native projects detected as 'unknown' platform, the logic treats both needsAndroid and needsIos as false, causing androidConfigured and iosConfigured to always be true regardless of whether configs exist. This means missing Firebase credentials go undetected in dual-native setups.

Since detectPlatform already calls detectNativePlatforms() to determine this case, use the same method in detectFirebaseConfig to set the actual needs when platform === 'unknown':

Suggested fix
export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> {
  const platform = await detectPlatform(projectPath);
+  const native =
+    platform === 'unknown'
+      ? await detectNativePlatforms(projectPath)
+      : { hasIos: false, hasAndroid: false };
  const expectedPaths = getExpectedPaths(platform, projectPath);

  const android = await detectAndroidCredential(projectPath, expectedPaths.android);
  const ios = await detectIosCredential(projectPath, expectedPaths.ios);

  const issues = generateIssues(android, ios, platform, expectedPaths);

  // Determine if Firebase is configured
-  const needsAndroid =
-    platform === 'android' || platform === 'react-native' || platform === 'flutter';
-  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
+  const needsAndroid =
+    platform === 'android' ||
+    platform === 'react-native' ||
+    platform === 'flutter' ||
+    (platform === 'unknown' && native.hasAndroid);
+  const needsIos =
+    platform === 'ios' ||
+    platform === 'react-native' ||
+    platform === 'flutter' ||
+    (platform === 'unknown' && native.hasIos);
🤖 Prompt for AI Agents
In `@src/lib/services/firebase/detector.ts` around lines 632 - 648,
detectFirebaseConfig currently treats an 'unknown' platform as not needing
Android or iOS, causing configured to be true when both configs are missing;
update detectFirebaseConfig to call detectNativePlatforms(projectPath) when
detectPlatform returns 'unknown' and use its result to set needsAndroid and
needsIos (instead of defaulting to false), so the boolean flags reflect actual
native platform presence before computing androidConfigured, iosConfigured and
configured; reference detectFirebaseConfig, detectPlatform,
detectNativePlatforms, needsAndroid, needsIos and getExpectedPaths when making
the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants