Skip to content

feat: Import Existing Projects (#134)#140

Open
cabana8471-arch wants to merge 9 commits intoAutoForgeAI:masterfrom
cabana8471-arch:feature/import-existing-projects
Open

feat: Import Existing Projects (#134)#140
cabana8471-arch wants to merge 9 commits intoAutoForgeAI:masterfrom
cabana8471-arch:feature/import-existing-projects

Conversation

@cabana8471-arch
Copy link
Contributor

@cabana8471-arch cabana8471-arch commented Jan 30, 2026

Summary

Closes #134 - Adds ability to import existing codebases into Autocoder.

  • Detect tech stack (React, Next.js, Vue, Nuxt, Node, Express, NestJS, Python, FastAPI, Django, Flask)
  • Extract features from routes, endpoints, and components
  • Multi-step import wizard with feature selection
  • Automatically generate test steps for each feature

Changes

Backend

  • analyzers/ - New module with stack detection and feature extraction
  • server/routers/import_project.py - REST API endpoints

Frontend

  • ui/src/components/ImportProjectModal.tsx - 6-step import wizard
  • ui/src/hooks/useImportProject.ts - Import workflow hook
  • ui/src/components/NewProjectModal.tsx - Add "Import Existing" option

Test plan

  • Select existing React/Next.js project → verify stack detection
  • Select existing Node/Express project → verify endpoint extraction
  • Select existing Python/FastAPI project → verify route extraction
  • Complete import → verify features appear in Kanban board
  • Lint passes: ruff check .
  • Type check passes: mypy .
  • UI builds: cd ui && npm run build

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Comprehensive project import flow: folder selection, tech detection, feature extraction, review/selection, registration, and creation.
  • Backend

    • New endpoints for project analysis, quick-detect previews, feature extraction, and feature creation.
  • Analyzers

    • New multi-language analyzers and a coordinator that detect stacks, routes, APIs, components and produce aggregated summaries and feature candidates.
  • UI

    • Import Project modal and hook implementing the multi-step wizard and feature selection UX.

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

cabana8471-arch and others added 3 commits January 30, 2026 23:56
Add analyzers module with support for:
- React/Next.js projects (App Router, Pages Router)
- Node.js/Express/NestJS/Fastify/Koa
- Python/FastAPI/Django/Flask
- Vue.js/Nuxt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Endpoints:
- POST /api/import/analyze - Detect tech stack
- POST /api/import/extract-features - Generate features
- POST /api/import/create-features - Create in database
- GET /api/import/quick-detect - Fast UI preview

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Closes AutoForgeAI#134

- Add ImportProjectModal with 6-step wizard
- Add useImportProject hook for state management
- Update NewProjectModal with project type selection
- Migrate styling to neobrutalism design system

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

coderabbitai bot commented Jan 30, 2026

Warning

Rate limit exceeded

@cabana8471-arch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds a suite of analyzers and a StackDetector to detect project stacks, a feature extractor to turn detections into Autocoder features, server endpoints to analyze/extract/create features, and a multi-step UI + hook to import projects and persist detected features.

Changes

Cohort / File(s) Summary
Analyzer Public Surface & Core
analyzers/__init__.py, analyzers/base_analyzer.py
Introduce public API exports and BaseAnalyzer with TypedDict shapes, file reading and file-finding helpers, and standardized AnalysisResult shape.
Stack Coordination
analyzers/stack_detector.py
Add StackDetector, StackInfo, StackDetectionResult, detect/detect_quick/to_json helpers to orchestrate multiple analyzers and aggregate results.
Feature Extraction
analyzers/feature_extractor.py
New feature-generation pipeline: DetectedFeature/FeatureExtractionResult types, feature generation from routes/endpoints/components, deduplication, aggregation, and conversion to MCP bulk-create format; includes extract_from_project wrapper.
Language / Framework Analyzers
analyzers/node_analyzer.py, analyzers/python_analyzer.py, analyzers/react_analyzer.py, analyzers/vue_analyzer.py
Add concrete analyzers (Node, Python, React/Next, Vue/Nuxt) implementing can_analyze/analyze, extracting routes/endpoints/components, entry points, configs, dependencies, and metadata flags.
Server Router & Endpoints
server/main.py, server/routers/__init__.py, server/routers/import_project.py
Register new import_project router and add /api/import endpoints for analyze, extract-features, create-features, and quick-detect with Pydantic models, validation, and DB integration points.
UI: Import Flow
ui/src/components/ImportProjectModal.tsx, ui/src/hooks/useImportProject.ts
Add ImportProjectModal multi-step UI and useImportProject hook to run analyze → extract → review → register flows, manage feature selection, and call server APIs.
UI: New Project Modal (Import Path)
ui/src/components/NewProjectModal.tsx
Refactor NewProjectModal to add a 'choose' step and integrate the import path via ImportProjectModal.
Server Router Exports
server/routers/__init__.py
Export import_project_router and add to all.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as "Browser UI"
    participant API as "FastAPI /api/import"
    participant Detector as "StackDetector"
    participant Analyzer as "Analyzers (Node/Python/React/Vue)"
    participant Extractor as "FeatureExtractor"
    participant DB as "Database"

    User->>UI: select folder
    UI->>API: POST /analyze
    API->>Detector: detect(project_dir)
    Detector->>Analyzer: can_analyze() / analyze()
    Analyzer-->>Detector: AnalysisResult
    Detector-->>API: StackDetectionResult
    API-->>UI: detected stacks & counts

    User->>UI: request feature extraction
    UI->>API: POST /extract-features
    API->>Extractor: extract_from_project(project_dir)
    Extractor->>Detector: uses detection result
    Extractor-->>API: FeatureExtractionResult
    API-->>UI: features by category

    User->>UI: create features
    UI->>API: POST /create-features
    API->>DB: create features (bulk)
    DB-->>API: creation summary
    API-->>UI: success
Loading
sequenceDiagram
    participant StackDetector
    participant NodeAnalyzer
    participant PythonAnalyzer
    participant ReactAnalyzer
    participant VueAnalyzer

    StackDetector->>NodeAnalyzer: can_analyze()
    alt confidence > 0.3
        StackDetector->>NodeAnalyzer: analyze()
        NodeAnalyzer-->>StackDetector: AnalysisResult
    end

    StackDetector->>PythonAnalyzer: can_analyze()
    alt confidence > 0.3
        StackDetector->>PythonAnalyzer: analyze()
        PythonAnalyzer-->>StackDetector: AnalysisResult
    end

    StackDetector->>ReactAnalyzer: can_analyze()
    alt confidence > 0.3
        StackDetector->>ReactAnalyzer: analyze()
        ReactAnalyzer-->>StackDetector: AnalysisResult
    end

    StackDetector->>VueAnalyzer: can_analyze()
    alt confidence > 0.3
        StackDetector->>VueAnalyzer: analyze()
        VueAnalyzer-->>StackDetector: AnalysisResult
    end

    StackDetector->>StackDetector: aggregate & pick primaries
    StackDetector-->>Caller: StackDetectionResult
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped through files, sniffed each stack and route,

Found pages, APIs, components — gave them a shout,
I stitched those findings into features, neat and small,
Packed them, queued them, sent them to the call,
A tiny rabbit's cheer — import, review, and install!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Import Existing Projects (#134)' clearly and directly summarizes the main change: importing existing projects into the system.
Linked Issues check ✅ Passed The PR implements all primary objectives from #134: project codebase analysis, feature detection across multiple stacks (React, Next.js, Vue, Nuxt, Node, Express, NestJS, Python, FastAPI, Django, Flask), and automatic feature creation for import.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the import feature: analyzers module for stack/feature detection, import API endpoints, multi-step UI wizard, and workflow hook. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.58% 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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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: 6

🤖 Fix all issues with AI agents
In `@analyzers/feature_extractor.py`:
- Around line 242-278: The feature name for endpoint-derived features is
generated with _route_to_feature_name(path, method) which yields page-style
names for non-/api paths (e.g., "/users" -> "View Users page"); force API-style
naming by calling a dedicated API naming path or helper: either call a
new/available _route_to_api_feature_name(path, method) or normalize the input to
_route_to_feature_name by prefixing the path with "/api" when path does not
start with "/api" before computing feature_name; update the feature_name
assignment (and optionally the description) where _route_to_feature_name is used
so endpoint features are always labeled as API features and keep the rest of the
feature object/population (features.append, seen_features logic) unchanged.

In `@analyzers/node_analyzer.py`:
- Around line 223-271: The _extract_nestjs_routes function constructs base_path
by unconditionally prepending "/" to controller_match.group(1), causing "//"
when the controller decorator already has a leading slash; update the logic that
computes base_path (look for controller_match and the variable base_path) to
strip any leading/trailing slashes from controller_match.group(1) first and then
prefix a single "/" (or use empty string when no controller path), and build
full_path by joining base_path and method path while ensuring you don't rely on
a trailing .replace("//", "/") hack so controllers without method paths don't
emit malformed routes.

In `@analyzers/react_analyzer.py`:
- Around line 173-213: _extract_app_router_routes currently only searches for
"page.tsx" and "page.jsx", missing "page.ts" and "page.js" files; update the
function to scan all four Next.js page filename patterns (page.js, page.jsx,
page.ts, page.tsx) — e.g., loop over a list of patterns or use a single glob
that matches those extensions — and reuse the same path-normalization (rel_path,
route_path, dynamic route regex, cleanup) and route dict creation (path, method,
handler, file using self.project_dir) for each matched file so JavaScript-only
and TypeScript-only pages are discovered too.

In `@analyzers/stack_detector.py`:
- Around line 95-105: The categorization logic for analyzer.stack_name only
matches exact base names, so variants like "react-vite", "react-cra",
"vue-vite", "vue-cli" or other stacks such as "nodejs", "fastify", and "koa"
fall into "other"; update the code that sets category to normalize and
pattern-match stack names instead of exact equality: convert analyzer.stack_name
to lower(), strip common suffixes/prefixes (e.g., "-vite", "-cra", "-cli"), and
then check membership or use startswith/contains for core identifiers ("react",
"vue", "next", "angular" → frontend; "express", "fastapi", "fastify", "django",
"flask", "nestjs", "node", "nodejs", "koa" → backend; "postgres", "mysql",
"mongodb", "sqlite" → database), assigning category accordingly to ensure
primary_frontend/primary_backend are set.

In `@server/routers/import_project.py`:
- Around line 40-45: The validate_path function is too permissive (it rejects
any substring ".." but misses path parts like "foo..bar") and the
/extract-features and /quick-detect handlers lack directory checks; update
validate_path to normalize and inspect path parts (e.g., use
pathlib.Path(path).parts) and explicitly reject any path part equal to ".." or
containing null bytes, and require absolute or expected base path semantics
consistent with the existing /analyze behavior; then add an is_dir() guard in
the extract-features and quick-detect handlers (functions named extract_features
and quick_detect) to verify the target is a directory before proceeding,
returning an appropriate error if not.

In `@ui/src/components/ImportProjectModal.tsx`:
- Around line 138-147: The Back/“Try Again” flow ignores the 'analyzing' state,
so update the handleBack function to handle step === 'analyzing' by resetting to
the folder selection: add an else-if branch for "analyzing" that calls
setStep('folder') and reset() (same behavior as the 'detected' branch) so the
button correctly backs out after a failed analysis; reference the handleBack
function, step variable, setStep and reset calls to locate where to add this
case.

- Fix API feature naming for non-/api paths
- Normalize NestJS controller paths to avoid // routes
- Include .js/.ts page files in Next.js route discovery
- Categorize stack variants (react-vite, vue-cli, etc.)
- Tighten path validation with Path.parts check
- Add is_dir() checks to extract-features/quick-detect
- Fix Back button in import modal for failed analysis

Co-Authored-By: Claude Opus 4.5 <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: 3

🤖 Fix all issues with AI agents
In `@ui/src/components/ImportProjectModal.tsx`:
- Line 12: The modal may leave a pending setTimeout that later calls
onProjectImported/handleClose after the modal has closed; update the
ImportProjectModal component to store the timeout id (e.g., in a ref or state)
when you call setTimeout (the timeout created in the import completion flow) and
clear it via clearTimeout both when the modal is programmatically closed
(handleClose) and in a useEffect cleanup/unmount; ensure any code paths that
call onProjectImported use the cleared/checked timeout so no callback fires
after unmount.
- Line 36: The Step union lacks an 'error' variant and the analyze/extract
failure paths never update step, so users get stuck; add 'error' to the Step
type (type Step = 'folder' | 'analyzing' | 'detected' | 'features' | 'register'
| 'complete' | 'error'), add a component state like errorMessage (or reuse
existing error state) and update all async failure handlers (the analyze/extract
functions that set step to 'analyzing' or 'detected' — e.g., the
analyzeProject/extractFeatures handlers referenced around lines 76-91) to set
step = 'error' and set the errorMessage; ensure the render logic for the 'error'
step displays the errorMessage so the error view becomes reachable.
- Around line 101-112: Validate projectName in handleRegisterAndCreate before
calling createProject.mutateAsync: add a regex test (matching the same pattern
used by the input control) and if it fails call setRegisterError with a clear
message and return early; ensure you still trim the name, and only proceed to
invoke createProject.mutateAsync with the validated trimmed name when the regex
passes.
🧹 Nitpick comments (6)
analyzers/react_analyzer.py (3)

148-171: Potential duplicate routes when both app/ and src/app/ exist.

If a project has both app/ and src/app/ directories (or both pages/ and src/pages/), routes from both will be collected, potentially leading to duplicates. Consider deduplicating or checking for this edge case.

♻️ Suggested approach
     def _extract_nextjs_routes(self) -> list[RouteInfo]:
         """Extract routes from Next.js file-based routing."""
         routes: list[RouteInfo] = []
+        seen_paths: set[str] = set()

         # Check for App Router (Next.js 13+)
         app_dir = self.project_dir / "app"
-        if app_dir.exists():
+        if app_dir.exists() and app_dir.is_dir():
             routes.extend(self._extract_app_router_routes(app_dir))
+            seen_paths.update(r["path"] for r in routes)

         # Check for Pages Router
         pages_dir = self.project_dir / "pages"
-        if pages_dir.exists():
-            routes.extend(self._extract_pages_router_routes(pages_dir))
+        if pages_dir.exists() and pages_dir.is_dir():
+            for route in self._extract_pages_router_routes(pages_dir):
+                if route["path"] not in seen_paths:
+                    routes.append(route)
+                    seen_paths.add(route["path"])

         # Also check src/app and src/pages (skip if root versions exist)
         src_app = self.project_dir / "src" / "app"
-        if src_app.exists():
-            routes.extend(self._extract_app_router_routes(src_app))
+        if src_app.exists() and src_app.is_dir() and not app_dir.exists():
+            routes.extend(self._extract_app_router_routes(src_app))

         src_pages = self.project_dir / "src" / "pages"
-        if src_pages.exists():
-            routes.extend(self._extract_pages_router_routes(src_pages))
+        if src_pages.exists() and src_pages.is_dir() and not pages_dir.exists():
+            routes.extend(self._extract_pages_router_routes(src_pages))

         return routes

232-262: Missing .tsx and .jsx extensions for App Router API routes.

Lines 257-260 only scan for route.ts and route.js, but Next.js App Router also supports route.tsx and route.jsx for API routes (though less common). For consistency with page file handling, consider including all extensions.

♻️ Suggested fix
         for app_api in app_api_dirs:
             if app_api.exists():
-                for route_file in app_api.rglob("route.ts"):
-                    endpoints.extend(self._parse_app_router_api(route_file, app_api))
-                for route_file in app_api.rglob("route.js"):
-                    endpoints.extend(self._parse_app_router_api(route_file, app_api))
+                for pattern in ("route.ts", "route.js", "route.tsx", "route.jsx"):
+                    for route_file in app_api.rglob(pattern):
+                        endpoints.extend(self._parse_app_router_api(route_file, app_api))

279-308: Consider handling named exports with const for App Router API methods.

The method detection pattern only matches export async function GET or export function GET. Next.js App Router also supports arrow function exports like export const GET = async (req) => {...}. This could miss valid API handlers.

♻️ Suggested fix to detect both patterns
         # Try to detect which methods are exported
         content = self._read_file_safe(route_file)
         methods = []
         if content:
             for method in ["GET", "POST", "PUT", "PATCH", "DELETE"]:
-                if f"export async function {method}" in content or \
-                   f"export function {method}" in content:
+                if (f"export async function {method}" in content or
+                    f"export function {method}" in content or
+                    f"export const {method}" in content):
                     methods.append(method)
analyzers/feature_extractor.py (2)

284-328: Consider expanding the skip list for common/generic components.

The current skip list (index, app, main, layout, _app, _document) covers common Next.js/React files but might miss other generic components that provide little value as features. Consider expanding this list.

♻️ Suggested expansion
         # Skip common/generic components
-        skip_names = ["index", "app", "main", "layout", "_app", "_document"]
+        skip_names = [
+            "index", "app", "main", "layout", "_app", "_document",
+            "header", "footer", "sidebar", "navbar", "nav",
+            "loading", "error", "not-found", "404", "500",
+        ]
         if name.lower() in skip_names:
             continue

326-348: Magic number for component feature limit.

The threshold of 10 features and the limit of 10 component features are magic numbers. Consider extracting these as named constants for clarity and easier adjustment.

♻️ Suggested refactor
+# Feature extraction thresholds
+MIN_FEATURES_BEFORE_COMPONENTS = 10
+MAX_COMPONENT_FEATURES = 10
+
 def extract_features(detection_result: StackDetectionResult) -> FeatureExtractionResult:
     ...
     # Add component features if we don't have many from routes/endpoints
-    if len(features) < 10:
-        features.extend(component_features[:10])  # Limit to 10 component features
+    if len(features) < MIN_FEATURES_BEFORE_COMPONENTS:
+        features.extend(component_features[:MAX_COMPONENT_FEATURES])
analyzers/stack_detector.py (1)

126-128: Use logging instead of print for warnings.

Line 128 uses print() for warning output. For consistency with production code and proper log management, use the logging module instead.

♻️ Suggested fix
+import logging
+
+logger = logging.getLogger(__name__)
+
 ...
                 except Exception as e:
                     # Log but don't fail - continue with other analyzers
-                    print(f"Warning: {analyzer.stack_name} analyzer failed: {e}")
+                    logger.warning(f"{analyzer.stack_name} analyzer failed: {e}")

- Add setTimeout cleanup to prevent callback after unmount
- Add 'error' to Step type for proper error state handling
- Validate projectName format before API call
- Deduplicate Next.js routes (prefer root over src/)
- Scan all extensions for App Router API routes (.tsx, .jsx)
- Detect const export pattern for API handlers
- Expand component skip list with common generic names
- Extract magic numbers as named constants
- Use logging instead of print for analyzer warnings

Co-Authored-By: Claude Opus 4.5 <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: 3

🤖 Fix all issues with AI agents
In `@analyzers/feature_extractor.py`:
- Around line 256-260: The code currently prefixes "/api" blindly causing "/api"
to become "/api/api"; fix by special-casing the root API path: if the incoming
path (variable path / name_path) equals "/api" or "/api/" (use rstrip to
normalize) leave name_path as "/api" (ensure it has a leading slash), otherwise
apply the existing prefix logic only when
name_path.lstrip("/").startswith("api") is false; then pass the normalized
name_path to _route_to_feature_name(name_path, method).

In `@analyzers/react_analyzer.py`:
- Around line 116-120: The entry-point detection loop in react_analyzer.py only
checks .tsx/.jsx files so self.project_dir / entry may leave entry_point unset
for pure .ts/.js projects; update the loop that iterates over the entry list to
include .ts and .js variants for common entries (e.g., add "src/main.ts",
"src/main.js", "src/index.ts", "src/index.js", and "pages/_app.ts",
"pages/_app.js") and remove the invalid "app/layout.ts" entry; ensure the code
still assigns entry_point when (self.project_dir / entry).exists().

In `@analyzers/stack_detector.py`:
- Around line 98-115: The stack categorization maps analyzer.stack_name to
category but currently omits the generic "python" value, causing
analyzer.stack_name == "python" to be classified as "other"; update the backend
branch that checks analyzer.stack_name (the tuple in the elif that sets category
= "backend") to include "python" among the listed backend identifiers so generic
Python projects are classified as backend (no other logic changes needed; note
stack_name is already lowercased).
🧹 Nitpick comments (4)
ui/src/components/ImportProjectModal.tsx (3)

267-268: Inconsistent error state check may cause unreliable error display.

The error render condition checks only state.step === 'error' (from the hook), but the component also manages a local step state that gets set to 'error' in handleFolderSelect and handleExtractFeatures. While the hook likely also sets its internal step to 'error', for consistency with the 'analyzing' step pattern (line 236), consider using a unified check.

🔧 Suggested fix for consistency
-  if (state.step === 'error') {
+  if (step === 'error' || state.step === 'error') {

474-480: Consider using a stable key instead of array index.

Using the array index i as the React key can cause issues if features are reordered or filtered. Since features have name and category, a composite key would be more stable.

🔧 Suggested fix
-                    {featuresByCategory[category]?.map((feature, i) => {
+                    {featuresByCategory[category]?.map((feature) => {
                       const isSelected = state.selectedFeatures.some(
                         f => f.name === feature.name && f.category === feature.category
                       )
                       return (
                         <div
-                          key={i}
+                          key={`${feature.category}-${feature.name}`}
                           onClick={() => toggleFeature(feature)}

199-206: Consider adding keyboard accessibility for the modal.

The modal closes on backdrop click but doesn't handle the Escape key. Adding keyboard support and ARIA attributes would improve accessibility.

🔧 Suggested improvement
+  // Handle Escape key to close modal
+  useEffect(() => {
+    const handleKeyDown = (e: KeyboardEvent) => {
+      if (e.key === 'Escape') {
+        handleClose()
+      }
+    }
+    if (isOpen) {
+      document.addEventListener('keydown', handleKeyDown)
+      return () => document.removeEventListener('keydown', handleKeyDown)
+    }
+  }, [isOpen])

   // Folder selection step
   if (step === 'folder') {
     return (
-      <div className="neo-modal-backdrop" onClick={handleClose}>
+      <div className="neo-modal-backdrop" onClick={handleClose} role="dialog" aria-modal="true" aria-labelledby="import-modal-title">
         <div
           className="neo-modal w-full max-w-3xl max-h-[85vh] flex flex-col"
           onClick={(e) => e.stopPropagation()}
         >
           <div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)]">
             <div className="flex items-center gap-3">
               <Folder size={24} className="text-[var(--color-neo-progress)]" />
               <div>
-                <h2 className="font-display font-bold text-xl text-[var(--color-neo-text)]">
+                <h2 id="import-modal-title" className="font-display font-bold text-xl text-[var(--color-neo-text)]">
                   Import Existing Project
                 </h2>
analyzers/react_analyzer.py (1)

312-358: Include .ts/.js files in React Router route discovery.

React Router's createBrowserRouter is commonly defined in plain .ts/.js files (using RouteObject arrays with Component/ErrorBoundary fields instead of JSX), but the current scan at line 317 only covers .tsx/.jsx files. This limitation causes route configurations in .ts/.js files to be missed.

Suggested update
-        route_files = self._find_files("**/*.tsx") + self._find_files("**/*.jsx")
+        route_files = (
+            self._find_files("**/*.tsx") + self._find_files("**/*.jsx") +
+            self._find_files("**/*.ts") + self._find_files("**/*.js")
+        )

- Fix API path double-prefixing bug (/api becoming /api/api)
- Add .ts/.js entry-point variants for pure JS/TS projects
- Classify Python stack as backend (not other)
- Fix inconsistent error state check in ImportProjectModal
- Use stable composite keys for feature list items
- Add Escape key support for modal accessibility
- Include .ts/.js files in React Router route discovery

Co-Authored-By: Claude Opus 4.5 <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: 2

🤖 Fix all issues with AI agents
In `@analyzers/react_analyzer.py`:
- Around line 372-386: The component and page discovery currently builds
component_files and page_files by calling _find_files with only **/*.tsx and
**/*.jsx patterns; update both calls to also include **/*.js (i.e., call
self._find_files("**/components/**/*.js") and
self._find_files("**/pages/**/*.js") alongside the existing patterns) so that
JavaScript files are included when populating components and pages (look for the
component_files, page_files variables and the _find_files helper).

In `@ui/src/components/ImportProjectModal.tsx`:
- Around line 113-175: Add an "active" ref guard to bail out of async handlers
when the modal is closed/unmounted: create a ref like isActiveRef and set
isActiveRef.current = true on mount/open and false on close/unmount (e.g., in
the modal's open/close lifecycle or useEffect cleanup). In handleFolderSelect,
handleExtractFeatures, and handleRegisterAndCreate check isActiveRef.current
immediately after each await (and before calling setStep, setProjectName,
setRegisterError, redirectTimeoutRef.current assignment, or calling
onProjectImported/handleClose) and return early if false; also clear any pending
redirectTimeoutRef when closing. Ensure you reference the existing
functions/values analyze, extractFeatures, createFeatures,
createProject.mutateAsync, state.projectPath, projectName, and handleClose so
the guard prevents stale state updates after the modal is closed.
🧹 Nitpick comments (1)
analyzers/feature_extractor.py (1)

120-123: Normalize stack variants before infra checks.

primary_frontend and primary_backend can be variant names (e.g., react-vite, vue-cli, fastify, koa, nodejs, python), so the infra features/steps won’t trigger. Consider normalizing or extending the lists.

🛠️ Proposed refactor
 def _generate_page_steps(path: str, stack: str | None) -> list[str]:
@@
-    if stack in ("react", "nextjs", "vue", "nuxt"):
+    base_stack = stack.split("-")[0] if stack else None
+    if base_stack in ("react", "nextjs", "vue", "nuxt", "angular"):
         steps.append("Verify no console errors in browser developer tools")
         steps.append("Verify responsive layout at mobile and desktop widths")
@@
 def _generate_basic_features(detection_result: StackDetectionResult) -> list[DetectedFeature]:
@@
-    if primary_frontend in ("react", "nextjs", "vue", "nuxt"):
+    frontend_base = primary_frontend.split("-")[0] if primary_frontend else None
+    if frontend_base in ("react", "nextjs", "vue", "nuxt", "angular"):
         features.append({
@@
-    if primary_backend in ("express", "fastapi", "django", "flask", "nestjs"):
+    backend_base = primary_backend.split("-")[0] if primary_backend else None
+    if backend_base in ("express", "fastify", "koa", "nodejs", "fastapi", "django", "flask", "nestjs", "python"):
         features.append({

Also applies to: 385-404

- Add .js files to component/page discovery in react_analyzer.py
- Normalize stack variants (react-vite, fastify, etc.) for infra features
- Add isActiveRef guard to prevent state updates after modal closes

Co-Authored-By: Claude Opus 4.5 <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 `@analyzers/react_analyzer.py`:
- Around line 249-255: The API route discovery loop in react_analyzer.py only
handles .ts and .js, missing .tsx and .jsx so Pages Router API routes are
skipped; update the loop that iterates api_dirs (the block that calls
self._parse_api_route for each api_file and extends endpoints) to include .tsx
and .jsx files as well (or refactor to iterate a list of extensions like
[".ts",".js",".tsx",".jsx"] and call self._parse_api_route for each match)
ensuring the endpoints list is populated from .tsx/.jsx files too.
🧹 Nitpick comments (7)
analyzers/react_analyzer.py (4)

117-125: Remove app/layout.ts from entry point candidates.

Next.js App Router only supports .js, .jsx, and .tsx extensions for layout files—not .ts. Including app/layout.ts adds an unnecessary file check that will never match a valid Next.js project.

♻️ Suggested fix
         for entry in [
             "src/main.tsx", "src/main.ts", "src/main.jsx", "src/main.js",
             "src/index.tsx", "src/index.ts", "src/index.jsx", "src/index.js",
             "pages/_app.tsx", "pages/_app.ts", "pages/_app.jsx", "pages/_app.js",
-            "app/layout.tsx", "app/layout.ts", "app/layout.jsx", "app/layout.js",
+            "app/layout.tsx", "app/layout.jsx", "app/layout.js",
         ]:

180-205: Potential duplicate routes when multiple page file extensions exist.

If a directory contains both page.tsx and page.js (e.g., during a migration), this will emit duplicate routes for the same path. Consider deduplicating by route path.

♻️ Optional: deduplicate by path
     def _extract_app_router_routes(self, app_dir: Path) -> list[RouteInfo]:
         """Extract routes from Next.js App Router."""
         routes: list[RouteInfo] = []
+        seen_paths: set[str] = set()

         # Check all page file extensions: .tsx, .jsx, .ts, .js
         for pattern in ("page.tsx", "page.jsx", "page.ts", "page.js"):
             for page_file in app_dir.rglob(pattern):
                 rel_path = page_file.relative_to(app_dir)
                 route_path = "/" + "/".join(rel_path.parent.parts)

                 # Handle dynamic routes: [id] -> :id
                 route_path = re.sub(r"\[([^\]]+)\]", r":\1", route_path)

                 # Clean up
                 if route_path == "/.":
                     route_path = "/"
                 route_path = route_path.replace("//", "/")

+                # Skip if we've already seen this route
+                if route_path in seen_paths:
+                    continue
+                seen_paths.add(route_path)
+
                 routes.append({
                     "path": route_path,
                     "method": "GET",
                     "handler": "Page",
                     "file": str(page_file.relative_to(self.project_dir)),
                 })

         return routes

293-304: Consider detecting HEAD and OPTIONS HTTP methods.

Next.js App Router also supports HEAD and OPTIONS exports. Currently only GET, POST, PUT, PATCH, DELETE are detected. This is a minor completeness gap.

♻️ Optional enhancement
-            for method in ["GET", "POST", "PUT", "PATCH", "DELETE"]:
+            for method in ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]:

328-337: Consider removing re.IGNORECASE from route patterns.

JSX/TSX is case-sensitive, and <Route> must be capitalized. The IGNORECASE flag on line 330 is unnecessary and could potentially match unintended patterns. The browser_router_pattern on line 334 also uses it unnecessarily for JavaScript object syntax.

♻️ Optional: remove IGNORECASE
         # Pattern for React Router <Route> elements
         route_pattern = re.compile(
-            r'<Route\s+[^>]*path=["\']([^"\']+)["\'][^>]*>',
-            re.IGNORECASE
+            r'<Route\s+[^>]*path=["\']([^"\']+)["\'][^>]*>'
         )

         # Pattern for createBrowserRouter routes
         browser_router_pattern = re.compile(
-            r'{\s*path:\s*["\']([^"\']+)["\']',
-            re.IGNORECASE
+            r'{\s*path:\s*["\']([^"\']+)["\']'
         )
ui/src/components/ImportProjectModal.tsx (3)

273-303: Missing accessibility attributes on analyzing modal.

The folder step (line 240) has role="dialog", aria-modal="true", and aria-labelledby, but the analyzing step modal (line 276) and other step modals lack these attributes. For consistent accessibility, all modal backdrops should have these attributes.

♻️ Add accessibility attributes
-      <div className="neo-modal-backdrop" onClick={handleClose}>
+      <div className="neo-modal-backdrop" role="dialog" aria-modal="true" onClick={handleClose}>

Apply this pattern to all modal backdrops (analyzing, error, detected, features, register, complete steps).


322-332: Handle missing error message gracefully.

Line 327 displays {state.error} which could be null or undefined if the error state was triggered without a message. Consider providing a fallback message.

♻️ Add fallback error message
-            <p className="text-[var(--color-neo-error-text)] mb-4">{state.error}</p>
+            <p className="text-[var(--color-neo-error-text)] mb-4">
+              {state.error || 'An unexpected error occurred. Please try again.'}
+            </p>

516-551: Add keyboard accessibility to feature selection.

The feature items use onClick on a div (line 519) but lack keyboard support. Users navigating with keyboards cannot select/deselect features. Consider adding role="checkbox", tabIndex={0}, and onKeyDown handler for Enter/Space.

♻️ Add keyboard accessibility
                         <div
                           key={`${feature.category}-${feature.name}`}
                           onClick={() => toggleFeature(feature)}
+                          onKeyDown={(e) => {
+                            if (e.key === 'Enter' || e.key === ' ') {
+                              e.preventDefault()
+                              toggleFeature(feature)
+                            }
+                          }}
+                          role="checkbox"
+                          aria-checked={isSelected}
+                          tabIndex={0}
                           className={`
                             flex items-start gap-3 p-3 cursor-pointer transition-all

Comment on lines +249 to +255
for api_dir in api_dirs:
if api_dir.exists():
for api_file in api_dir.rglob("*.ts"):
endpoints.extend(self._parse_api_route(api_file, api_dir))
for api_file in api_dir.rglob("*.js"):
endpoints.extend(self._parse_api_route(api_file, api_dir))

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

Missing .tsx/.jsx extensions for Pages Router API routes.

Lines 251-254 only scan .ts and .js files, but Pages Router API routes can also use .tsx and .jsx extensions. This is inconsistent with other file discovery patterns in this analyzer.

🐛 Proposed fix
         for api_dir in api_dirs:
             if api_dir.exists():
-                for api_file in api_dir.rglob("*.ts"):
-                    endpoints.extend(self._parse_api_route(api_file, api_dir))
-                for api_file in api_dir.rglob("*.js"):
-                    endpoints.extend(self._parse_api_route(api_file, api_dir))
+                for ext in ("ts", "tsx", "js", "jsx"):
+                    for api_file in api_dir.rglob(f"*.{ext}"):
+                        endpoints.extend(self._parse_api_route(api_file, api_dir))
🤖 Prompt for AI Agents
In `@analyzers/react_analyzer.py` around lines 249 - 255, The API route discovery
loop in react_analyzer.py only handles .ts and .js, missing .tsx and .jsx so
Pages Router API routes are skipped; update the loop that iterates api_dirs (the
block that calls self._parse_api_route for each api_file and extends endpoints)
to include .tsx and .jsx files as well (or refactor to iterate a list of
extensions like [".ts",".js",".tsx",".jsx"] and call self._parse_api_route for
each match) ensuring the endpoints list is populated from .tsx/.jsx files too.

cabana8471-arch and others added 2 commits January 31, 2026 01:55
- Remove app/layout.ts from entry points (not valid in Next.js)
- Deduplicate App Router routes when multiple extensions exist
- Add HEAD and OPTIONS HTTP method detection
- Remove unnecessary re.IGNORECASE from route patterns
- Add accessibility attributes to all modal backdrops
- Add fallback error message for error state
- Add keyboard accessibility to feature selection checkboxes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Renamed 'content' to 'main_content' and 'app_content' in python_analyzer.py
  to avoid type narrowing conflicts with earlier str-typed variable
- Added type annotation for _analyzers list in stack_detector.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
rudiheydra added a commit to rudiheydra/AutoBuildr that referenced this pull request Feb 2, 2026
…lidator (Feature AutoForgeAI#140)

- Added LintCleanValidator to api/validators.py following existing validator pattern
- Accepts linter command in config, runs it, checks exit code for zero errors
- Supports error_pattern regex for counting lint issues in output
- Registered in VALIDATOR_REGISTRY so it can be resolved by name
- Exported from api/__init__.py
- 52 tests in test_feature_140_lint_clean_validator.py all passing
- Verified with sample linter commands (flake8, ruff-style output)
- No regressions in existing 172 validator tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
rudiheydra added a commit to rudiheydra/AutoBuildr that referenced this pull request Feb 2, 2026
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@chrislangston
Copy link
Contributor

@cabana8471-arch this would be a sweet feature!

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.

Feature request: Jump into existing project

2 participants