Skip to content

Conversation

@AbianS
Copy link
Owner

@AbianS AbianS commented Jan 14, 2026

  • Updated the editor layout for better usability.
  • Modified sidebar to include a project switch button.
  • Adjusted window capabilities to support multiple windows.

Summary by CodeRabbit

  • New Features

    • Introduced Studio desktop application for managing code recall projects with a modern UI
    • Added project launcher with search, vector count display, and project status indicators
    • Integrated editor interface with tabbed navigation (Overview, Memory Graph, Rules Engine, Cognitive Logs)
    • Added project management capabilities (add/remove projects, view database statistics)
  • Documentation

    • Restructured README to focus on TurboRepo monorepo setup and development workflows
  • Chores

    • Restructured project as monorepo with server and studio applications
    • Updated CI/CD workflows and build configuration
    • Enhanced development tooling and IDE configurations

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

AbianS added 13 commits January 14, 2026 12:18
* Introduce tests for RulesEngine with various scenarios:
  - Adding rules with different configurations.
  - Checking rules against actions and verifying matches.
  - Listing and retrieving rules.
  - Clearing cache and ensuring embeddings are regenerated.

* Implement tests for MemoryManager functionalities:
  - Storing and searching observations.
  - Recording outcomes and retrieving stats.
  - Ensuring proper linking between memories and code entities.

* Create setup utilities for test database management.

* Add TypeScript configuration for server application.

* Update package.json for monorepo structure and dependencies.
* Modify package.json scripts to use bun commands
* Add lint and lint:fix scripts to package.json
* Update turbo.json outputs for build and lint tasks
* Rename job from 'test' to 'ci' in ci.yml
* Ensure consistent command usage with 'bun run test'
* Clean up whitespace in release.yml
* Remove unnecessary class from main container in App.tsx
* Add Tailwind CSS plugin to Vite configuration
* Update TypeScript and other dependencies in bun.lock and package.json
* Upgrade various packages to their latest versions for improved performance and compatibility
* Introduced Tailwind CSS with custom styles and configurations.
* Added new Button component with variant support.
* Created utility function for class name merging.
* Updated TypeScript configuration for path aliases.
* Enhanced Vite configuration with path resolution.
* Updated dependencies in package.json and bun.lock.
* Added comprehensive CSS styles for dark mode and theming.
* Updated dependencies to use exact versions instead of ranges
* Ensures consistent installations across environments
* Update index.html to reflect new app title.
* Add Footer component for database and preferences actions.
* Implement ProjectCard and ProjectList components for project display.
* Introduce SearchInput for filtering projects.
* Add status badges for project statuses.
* Update styles for dark mode and UI components.
* Integrate new Radix UI scroll area component.
* Update package.json and bun.lock to include new dependencies.
* Add commands for managing projects and database stats
* Integrate dialog for opening database files
* Update project and database models
* Enhance App component to load and manage projects
* Update dependencies in package.json and bun.lock
* Modify VSCode settings for Rust support
* Integrate Radix UI's Alert Dialog component for confirming project deletions.
* Update ProjectCard to include a delete button that triggers the alert dialog.
* Modify ProjectList to pass delete handler to ProjectCard.
* Implement state management for the project to be deleted in App component.
* Ensure the project deletion logic updates the project list and selected project state accordingly.
* Add editor HTML and main entry point
* Create Editor component to display project details
* Integrate editor opening functionality in commands
* Update Tauri configuration for new window
* Modify Vite config for multi-page setup
* Enhance project selection to open editor on click
* Implement sidebar component for project navigation
* Add pages for Overview, Memory Graph, Rules Engine, and Cognitive Logs
* Update Editor component to manage active tab state
* Enhance styling with new surface color variables
* Updated the editor layout for better usability.
* Modified sidebar to include a project switch button.
* Adjusted window capabilities to support multiple windows.
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Walkthrough

This pull request transforms the project into a monorepo structure using Turborepo, introducing a new Tauri-based studio application (apps/studio) with React frontend and Rust backend, plus a separate server application (apps/server). Updates include CI/CD workflows, root configuration files, comprehensive TypeScript and CSS setup, and database/project management functionality.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Renamed test job to ci; updated test command from bun test to bun run test; refined release trigger pattern and publish step targeting (apps/server).
Root Monorepo Setup
package.json, turbo.json, biome.json, README.md, .gitignore
Converted to monorepo with Turborepo; added workspace configuration (apps/*); updated build/test/lint scripts to use turbo; expanded .gitignore patterns; rewrote README for Turbo-centric workflow.
VSCode Configuration
.vscode/extensions.json, .vscode/settings.json
Added extension recommendations (Tauri, Rust analyzer) and Rust formatter/biome settings with Cargo.toml project linking.
Server App (Code Recall)
apps/server/.gitignore, apps/server/README.md, apps/server/package.json
New server application with AI/ML dependencies, TypeScript setup, bun engine requirement, and comprehensive documentation.
Studio App Configuration
apps/studio/{package.json, tsconfig*.json, vite.config.ts, components.json, index.html, editor.html, .gitignore}
Set up React + TypeScript + Vite + Tailwind project with shadcn/ui; configured dual entry points (launcher, editor); defined build and development workflows.
Studio Tauri Backend (Rust)
apps/studio/src-tauri/{Cargo.toml, build.rs, tauri.conf.json, capabilities/default.json}, apps/studio/src-tauri/src/{main.rs, lib.rs, commands.rs, models.rs, database.rs, store.rs, window.rs}
Complete Tauri application with SQLite integration, project persistence via store, database stats retrieval, editor window management, and IPC command handlers.
Studio React Frontend - Core
apps/studio/src/{main.tsx, vite-env.d.ts, App.tsx, App.css}, apps/studio/src/lib/{utils.ts, tauri.ts}
Project launcher UI with search, filtering, and deletion; Tauri IPC wrappers; dark theme CSS with Tailwind variables.
Studio React Frontend - Editor Pages
apps/studio/src/editor/{main.tsx, Editor.tsx, components/sidebar.tsx}, apps/studio/src/editor/pages/{overview.tsx, memory-graph.tsx, rules-engine.tsx, cognitive-logs.tsx}
Multi-tab editor interface with project navigation, tab switching, and placeholder content for four editor sections.
Studio React Frontend - UI Components
apps/studio/src/components/ui/{button.tsx, input.tsx, alert-dialog.tsx, scroll-area.tsx}
Reusable, styled UI primitives wrapping Radix UI with cva-based variants and Tailwind integration.
Studio React Frontend - Launcher Components
apps/studio/src/components/launcher/{index.ts, footer.tsx, project-card.tsx, project-list.tsx, search-input.tsx, status-badge.tsx}
Project card list with filtering, project status indicators, and action buttons (open database, preferences, documentation).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App as Launcher App
    participant Tauri as Tauri Backend
    participant Store as Project Store
    participant DB as SQLite DB

    User->>App: Load launcher
    App->>Tauri: getProjects()
    Tauri->>Store: Read projects list
    Store-->>Tauri: Return stored projects
    loop For each project
        Tauri->>DB: COUNT non-archived memories
        DB-->>Tauri: Return memory count
    end
    Tauri-->>App: Return projects with stats
    App->>App: Render project list
    User->>User: View projects
Loading
sequenceDiagram
    participant User
    participant Launcher as Launcher UI
    participant Tauri as Tauri Backend
    participant Dialog as File Dialog
    participant Store as Project Store

    User->>Launcher: Click "Open Database"
    Launcher->>Tauri: openDatabaseDialog()
    Tauri->>Dialog: Show file picker
    User->>Dialog: Select SQLite file
    Dialog-->>Tauri: Return file path
    Tauri->>Store: addProject(path)
    Store->>Store: Generate UUID, extract name
    Store->>Store: Serialize & save projects.json
    Tauri-->>Launcher: Return StoredProject
    Launcher->>Launcher: Update projects list
    Launcher->>Launcher: Load database stats
    Launcher->>Launcher: Re-render with new project
    User->>User: See new project in list
Loading
sequenceDiagram
    participant User
    participant Launcher as Launcher UI
    participant Tauri as Tauri Backend
    participant WindowMgr as Window Manager
    participant Editor as Editor UI

    User->>Launcher: Select project & click open
    Launcher->>Tauri: openEditorWindow(name, path)
    Tauri->>WindowMgr: Hide launcher window
    alt Editor window exists
        WindowMgr->>Editor: Navigate to URL with params
        Editor->>Editor: Update title & content
        WindowMgr->>Editor: Show & focus
    else Create new editor
        WindowMgr->>Editor: Create new window
        Editor->>Editor: Load Editor component
        Editor->>Editor: Parse URL params (name, path)
        Editor->>Editor: Render with sidebar & tabs
    end
    Tauri-->>Launcher: Return success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: turborepo + studio app' accurately describes the main changes: migration to Turborepo for monorepo management and creation of a new studio app with Tauri, UI components, and editor functionality.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@AbianS AbianS marked this pull request as draft January 14, 2026 21:34
@AbianS AbianS changed the title feat: turborepo feat: turborepo + studio app Jan 14, 2026
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: 12

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Around line 22-27: The release workflow publishes apps/server without running
its build, so add a build step before the "Publish server to npm" job: insert a
step named like "Build server" that runs the server build command (e.g., run:
bun run build) with working-directory: apps/server, ensuring it runs after "Run
tests" and before the publish step so the dist artifacts are produced before bun
publish; if the package.json build script uses a different command name, call
that script instead.

In `@apps/studio/src-tauri/src/commands.rs`:
- Around line 8-20: open_database_dialog currently calls
app.dialog().file().blocking_pick_file() inside an async command which will
block the Tauri async runtime; change it to use the non-blocking API or offload
the blocking call: either replace blocking_pick_file() with the callback variant
app.dialog().file().pick_file(|path| { /* convert path to String and return via
a oneshot or emit event */ }) and adapt the function to return via the callback,
or wrap the blocking call using tauri::async_runtime::spawn_blocking(move ||
app.dialog().file().blocking_pick_file()).await and then convert the result to
Result<Option<String>, String> before returning from open_database_dialog.

In `@apps/studio/src-tauri/src/database.rs`:
- Around line 7-13: The query for memory_count is silently swallowing errors via
unwrap_or(0) which masks schema or other DB errors; replace the unwrap_or(0)
with proper error propagation from conn.query_row (e.g., return a Result from
the surrounding function or use map_err/? on the query), or explicitly match the
query_row Result to return 0 only for a "no such table" / missing-table error
and propagate all other errors; update any function signatures that use
memory_count (and references to conn.query_row) to handle the propagated error
accordingly and add a short comment if returning 0 for absent tables is
intentional.

In `@apps/studio/src-tauri/tauri.conf.json`:
- Around line 24-26: The configuration currently disables Content Security
Policy by setting the "security.csp" value to null; replace this with a
restrictive, explicit CSP string instead of null (update the "security" object
key "csp") to re-enable XSS and injection protections—define a baseline policy
that restricts default-src to the app itself, explicitly allow any external
resources you require (e.g., scripts, styles, images, fonts) and only permit
inline or eval features if absolutely necessary, then test the app and
iteratively relax directives for required external origins or data: sources.

In `@apps/studio/src/App.css`:
- Around line 118-129: The CSS defines --status-* and --traffic-* variables only
inside the .dark selector so they are undefined in light mode; add matching
variable definitions to the :root block (using appropriate light-mode values) so
--status-active, --status-idle, --status-sync, --status-archived,
--status-offline and --traffic-red/--traffic-yellow/--traffic-green are
available outside .dark and will not fall back to unexpected values.

In `@apps/studio/src/App.tsx`:
- Around line 114-130: handleConfirmDelete uses the closed-over projects array
which may be stale after the async removeProject; update the logic to compute
remaining projects from the latest state by using functional state updates: call
setProjects(prev => { const updated = prev.filter(p => p.id !==
projectToDelete.id); if (selectedProjectId === projectToDelete.id)
setSelectedProjectId(updated[0]?.id); return updated; }); keep removeProject
call and error handling the same, and remove the direct reference to the outer
projects variable.

In `@apps/studio/src/editor/components/sidebar.tsx`:
- Around line 34-37: The handleSwitchProject function currently awaits
window.close() without error handling; wrap the async close call in a try/catch
inside handleSwitchProject, check that getCurrentWindow() returned a valid
window before calling close (e.g., guard with if (window)), await window.close()
inside the try, and log or surface any caught error (using existing logger or
console.error) to avoid unhandled promise rejections.

In `@apps/studio/src/lib/tauri.ts`:
- Around line 33-41: The invoke call in openEditorWindow uses camelCase keys
(projectName, projectPath) but the Rust tauri command expects snake_case
(project_name, project_path); update the invoke payload in openEditorWindow to
send project_name mapped to projectName and project_path mapped to projectPath
so the parameter names match Rust, or alternatively rename the Rust command
parameters to camelCase to match the current TS keys (preferred: change the
invoke keys to snake_case).

In `@package.json`:
- Around line 2-22: Update package.json to use valid dependency versions and
align the Bun constraint: replace the non-existent "turbo": "2.7.4" with
"turbo": "2.5.6" and "@biomejs/biome": "2.3.11" with "@biomejs/biome": "2.2.3"
in devDependencies, and then make engines.bun consistent with packageManager by
changing engines.bun to ">=1.3.5" (or alternatively update packageManager to the
broader version you intend to support and document why); ensure the changes are
only made in the devDependencies and engines/packageManager fields so package
resolution and engine enforcement match.

In `@README.md`:
- Around line 1-135: The README.md is a stale Turborepo template that doesn't
reflect this repo: update sections describing packages/apps to list the actual
apps (`apps/studio`, `apps/server`) and remove or replace references to `docs`,
`web`, `@repo/*` packages; update the tooling section to mention Biome as used
in the root package.json instead of ESLint/Prettier; replace generic "cd
my-turborepo" examples with the appropriate project name or remove the redundant
cd lines; and add language specifiers (e.g., sh or bash) to the fenced code
blocks flagged (around the build/dev/usage examples) so syntax highlighting
renders correctly.
🧹 Nitpick comments (23)
.vscode/settings.json (1)

2-11: Consider adding the Biome extension to recommendations.

The settings reference biomejs.biome as the default formatter, but this extension is not included in .vscode/extensions.json. Consider adding it to the recommendations for a consistent developer experience.

// In .vscode/extensions.json
{
  "recommendations": [
    "tauri-apps.tauri-vscode",
    "rust-lang.rust-analyzer",
    "biomejs.biome"
  ]
}

Otherwise, the configuration looks good—language-specific overrides for Rust and organized imports on save are well-structured.

apps/studio/src/App.css (1)

131-145: Consider consolidating duplicate @layer base blocks.

The two @layer base blocks can be merged into one for better organization.

♻️ Suggested consolidation
 `@layer` base {
   * {
     `@apply` border-border outline-ring/50;
   }
   body {
     `@apply` bg-background text-foreground;
   }
-}
-
-@layer base {
   button:not(:disabled),
   [role="button"]:not(:disabled) {
     cursor: pointer;
   }
 }
apps/studio/.gitignore (1)

1-24: Consider adding .env patterns for defense in depth.

While the root .gitignore covers environment files, adding explicit .env* patterns here would follow best practices for:

  • Defense in depth security
  • App portability (if ever separated from monorepo)
  • Clear documentation of ignored files
🔒 Proposed addition of environment file patterns
 *.local
+
+# Environment variables
+.env
+.env*.local
.gitignore (1)

1-40: LGTM! Well-organized root gitignore.

The reorganization with clear section labels improves maintainability. The addition of .env.local (line 10) completes environment file coverage, and *.pem (line 38) appropriately ignores SSL certificates and private keys—a good security practice.

The Turbo and Vercel sections are well-suited for the new monorepo structure.

📝 Optional: Consider adding TypeScript build info at root level

If TypeScript is used at the workspace root, consider adding:

 # Misc

 .DS_Store
 *.pem
+*.tsbuildinfo

This is already covered in apps/server/.gitignore, so only add if needed at the root level.

apps/server/README.md (1)

64-69: Consider adding language specifiers to code blocks.

The fenced code blocks at lines 64, 221, and 290 lack language specifiers. While these contain pseudo-code/diagrams rather than actual code, adding a specifier like text or plaintext would satisfy linters and improve consistency.

Example fix for line 64
-```
+```text
 Trigger: "adding API endpoint"
 → Must do: ["Add rate limiting", "Write integration tests"]
apps/studio/src/editor/pages/memory-graph.tsx (1)

1-15: LGTM!

Consistent structure with other editor page placeholders.

Consider extracting the shared placeholder layout into a reusable component since CognitiveLogs, MemoryGraph, and likely other pages share identical structure (icon container, heading, subtitle). This would reduce duplication when adding more placeholder pages.

♻️ Optional: Shared placeholder component
// apps/studio/src/editor/components/placeholder-page.tsx
import type { LucideIcon } from "lucide-react";

interface PlaceholderPageProps {
  icon: LucideIcon;
  title: string;
  subtitle: string;
}

export function PlaceholderPage({ icon: Icon, title, subtitle }: PlaceholderPageProps) {
  return (
    <div className="h-full w-full flex flex-col items-center justify-center gap-4 bg-surface">
      <div className="w-16 h-16 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center">
        <Icon className="w-8 h-8 text-primary" />
      </div>
      <h1 className="text-2xl font-semibold text-foreground">{title}</h1>
      <p className="text-sm text-muted-foreground">{subtitle}</p>
    </div>
  );
}

Then usage becomes:

import { Network } from "lucide-react";
import { PlaceholderPage } from "../components/placeholder-page";

export function MemoryGraph() {
  return (
    <PlaceholderPage
      icon={Network}
      title="Memory Graph"
      subtitle="Visual representation of memories and connections"
    />
  );
}
apps/studio/vite.config.ts (1)

9-46: Consider removing unnecessary async wrapper.

The defineConfig callback is marked as async but contains no await expressions. While this works, removing the async keyword makes the intent clearer.

Suggested simplification
-export default defineConfig(async () => ({
+export default defineConfig(() => ({
   plugins: [react(), tailwindcss()],
apps/studio/package.json (2)

16-16: Move @tailwindcss/vite to devDependencies.

The @tailwindcss/vite plugin is a build-time dependency used by Vite during development and bundling. It should be in devDependencies rather than dependencies.

♻️ Suggested fix
   "dependencies": {
     "@radix-ui/react-alert-dialog": "1.1.15",
     "@radix-ui/react-scroll-area": "^1.2.10",
     "@radix-ui/react-slot": "1.2.4",
-    "@tailwindcss/vite": "4.1.18",
     "@tauri-apps/api": "2.9.1",
     ...
   },
   "devDependencies": {
+    "@tailwindcss/vite": "4.1.18",
     "@tauri-apps/cli": "2.9.6",
     ...
   }

12-27: Inconsistent version pinning strategy.

Some dependencies use exact versions (e.g., "react": "19.2.3") while others use caret ranges (e.g., "@radix-ui/react-scroll-area": "^1.2.10"). Consider adopting a consistent approach for reproducible builds.

apps/studio/src/components/launcher/footer.tsx (1)

24-33: Consider visual feedback when callback is not provided.

When onOpenDatabase is undefined, clicking the button silently does nothing. Consider adding a disabled state or ensuring the callback is always provided to avoid confusing UX.

♻️ Optional: Add disabled state
       <button
         type="button"
         onClick={onOpenDatabase}
-        className="w-full group flex items-center justify-center gap-2 py-2.5 rounded-lg border border-dashed border-border hover:border-primary/40 hover:bg-primary/5 transition-all mb-4"
+        disabled={!onOpenDatabase}
+        className="w-full group flex items-center justify-center gap-2 py-2.5 rounded-lg border border-dashed border-border hover:border-primary/40 hover:bg-primary/5 transition-all mb-4 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-border disabled:hover:bg-transparent"
       >
apps/studio/src/components/ui/button.tsx (1)

39-60: Consider defaulting type="button" to prevent accidental form submissions.

Buttons without an explicit type attribute default to type="submit" in HTML. When a Button is placed inside a form without specifying the type, it will unexpectedly submit the form on click.

Suggested fix
 function Button({
   className,
   variant = "default",
   size = "default",
   asChild = false,
+  type = "button",
   ...props
 }: React.ComponentProps<"button"> &
   VariantProps<typeof buttonVariants> & {
     asChild?: boolean;
   }) {
   const Comp = asChild ? Slot : "button";

   return (
     <Comp
       data-slot="button"
       data-variant={variant}
       data-size={size}
+      type={asChild ? undefined : type}
       className={cn(buttonVariants({ variant, size, className }))}
       {...props}
     />
   );
 }
apps/studio/src-tauri/src/window.rs (4)

14-27: Refactor: Extract duplicated URL construction logic.

The URL construction logic is duplicated between the "navigate existing editor" path (Lines 14-27) and the "create new editor" path (Lines 41-54). Consider extracting this into a helper function.

♻️ Proposed refactor
+fn build_editor_url(project_name: &str, project_path: &str) -> String {
+    let encoded_name = urlencoding::encode(project_name);
+    let encoded_path = urlencoding::encode(project_path);
+
+    #[cfg(debug_assertions)]
+    {
+        format!(
+            "http://localhost:1420/editor.html?name={}&path={}",
+            encoded_name, encoded_path
+        )
+    }
+
+    #[cfg(not(debug_assertions))]
+    {
+        format!(
+            "tauri://localhost/editor.html?name={}&path={}",
+            encoded_name, encoded_path
+        )
+    }
+}

Then use let url = build_editor_url(project_name, project_path); in both locations.

Also applies to: 41-54


33-33: Potential panic: unwrap() on URL parse.

Using .unwrap() on url.parse() could panic if the URL is malformed. While the format string is controlled, unexpected characters in project_name or project_path after encoding could theoretically cause issues. Consider using map_err for consistency with the rest of the error handling.

♻️ Proposed fix
-        editor
-            .navigate(url.parse().unwrap())
-            .map_err(|e| e.to_string())?;
+        let parsed_url = url.parse().map_err(|e: url::ParseError| e.to_string())?;
+        editor
+            .navigate(parsed_url)
+            .map_err(|e| e.to_string())?;

58-62: Same issue: unwrap() on URL parse when building WebviewWindow.

Line 61 has the same potential panic risk with .unwrap() on URL parsing.

♻️ Proposed fix
+    let parsed_url: url::Url = url.parse().map_err(|e: url::ParseError| e.to_string())?;
+
     WebviewWindowBuilder::new(
         app,
         EDITOR_LABEL,
-        WebviewUrl::External(url.parse().unwrap()),
+        WebviewUrl::External(parsed_url),
     )

69-73: Silent error suppression in window event handler.

The let _ = show_launcher(&app_handle); discards any errors from showing the launcher. While this may be intentional (avoiding blocking the close), consider logging the error for debugging purposes.

♻️ Suggested improvement
     .on_window_event(move |event| {
         if let tauri::WindowEvent::CloseRequested { .. } = event {
-            let _ = show_launcher(&app_handle);
+            if let Err(e) = show_launcher(&app_handle) {
+                eprintln!("Failed to show launcher: {}", e);
+            }
         }
     });
apps/studio/src/components/ui/alert-dialog.tsx (1)

118-140: Missing data-slot attributes on AlertDialogAction and AlertDialogCancel.

For consistency with other components in this file (which all have data-slot attributes for telemetry/DOM targeting), consider adding them to AlertDialogAction and AlertDialogCancel.

♻️ Proposed fix
 function AlertDialogAction({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
   return (
     <AlertDialogPrimitive.Action
+      data-slot="alert-dialog-action"
       className={cn(buttonVariants(), className)}
       {...props}
     />
   );
 }

 function AlertDialogCancel({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
   return (
     <AlertDialogPrimitive.Cancel
+      data-slot="alert-dialog-cancel"
       className={cn(buttonVariants({ variant: "outline" }), className)}
       {...props}
     />
   );
 }
apps/studio/src/components/launcher/project-list.tsx (1)

10-39: LGTM! Well-structured list component with consistent grid layout.

The grid column layout (5 + 2 + 3 + 2 = 12) correctly aligns with ProjectCard's internal layout. The use of optional chaining for callbacks (onSelect?.(), onDelete?.()) is appropriate, and project.id as the key ensures proper reconciliation.

Consider adding an empty state UI when projects.length === 0 for better UX.

apps/studio/src/editor/Editor.tsx (1)

14-25: Consider initializing state from URL params synchronously to avoid flash of empty state.

The current approach initializes projectName to an empty string, then updates it in useEffect. This causes a brief render with empty values before the URL params are parsed.

♻️ Optional: Initialize from URL params directly
+const getInitialParams = () => {
+  const params = new URLSearchParams(window.location.search);
+  return {
+    name: params.get("name") || "Unknown Project",
+    path: params.get("path") || "",
+  };
+};
+
 function Editor() {
-  const [projectName, setProjectName] = useState<string>("");
-  const [projectPath, setProjectPath] = useState<string>("");
+  const [projectName, setProjectName] = useState<string>(() => getInitialParams().name);
+  const [projectPath, setProjectPath] = useState<string>(() => getInitialParams().path);
   const [activeTab, setActiveTab] = useState<TabId>("overview");
-
-  useEffect(() => {
-    const params = new URLSearchParams(window.location.search);
-    const name = params.get("name") || "Unknown Project";
-    const path = params.get("path") || "";
-    setProjectName(name);
-    setProjectPath(path);
-  }, []);
apps/studio/src/App.tsx (2)

38-79: Potential infinite re-render loop due to selectedProjectId dependency.

loadProjects includes selectedProjectId in its dependency array, and it also calls setSelectedProjectId. This creates a risk: when loadProjects sets selectedProjectId, it recreates loadProjects, triggering the useEffect again.

Currently safe because of the !selectedProjectId guard (line 67), but the dependency is unnecessary—selectedProjectId is only read to decide whether to set it, not to compute the projects.

♻️ Suggested improvement: Remove unnecessary dependency
-  const loadProjects = useCallback(async () => {
+  const loadProjects = useCallback(async (currentSelectedId?: string) => {
     try {
       const storedProjects = await getProjects();
       // ... mapping logic ...
       setProjects(projectsWithStats);

-      if (projectsWithStats.length > 0 && !selectedProjectId) {
+      if (projectsWithStats.length > 0 && !currentSelectedId) {
         setSelectedProjectId(projectsWithStats[0].id);
       }
     } catch (error) {
       console.error("Failed to load projects:", error);
     } finally {
       setIsLoading(false);
     }
-  }, [selectedProjectId]);
+  }, []);

   useEffect(() => {
-    loadProjects();
-  }, [loadProjects]);
+    loadProjects(selectedProjectId);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []); // Run only on mount

132-138: Placeholder implementations for preferences and documentation.

These handlers only log to console. Consider adding TODO comments or removing them until implemented to avoid confusion.

apps/studio/src-tauri/src/commands.rs (1)

22-25: Consider validating the database path before opening.

The path parameter is passed directly to database::get_stats without validation. While the dialog provides some protection, this command can be invoked directly from the frontend with any path. Consider:

  1. Validating the path exists and is a file
  2. Ensuring it has an expected extension (.db, .sqlite)
  3. Canonicalizing the path to prevent traversal
🔒 Suggested validation
#[tauri::command]
pub fn get_database_stats(path: String) -> Result<DatabaseStats, String> {
    let path = std::path::Path::new(&path);
    
    if !path.exists() {
        return Err("Database file does not exist".to_string());
    }
    
    if !path.is_file() {
        return Err("Path is not a file".to_string());
    }
    
    database::get_stats(path.to_str().unwrap_or_default())
}
apps/studio/src-tauri/src/store.rs (2)

36-69: Potential race condition: concurrent add_project calls could add duplicates.

If two add_project calls occur concurrently with the same path, both might pass the duplicate check (line 54) before either writes, resulting in duplicate entries.

For a desktop app with single-user access, this is unlikely but possible if the user rapidly clicks "Add".

Consider using a mutex or file lock if this becomes an issue, or debouncing on the frontend.


71-92: remove_project silently succeeds when ID doesn't exist.

The retain call (line 81) will simply not remove anything if the ID doesn't match. This is idempotent behavior which is generally good, but the caller won't know if the project was actually removed.

If this is intentional (idempotent delete), consider documenting it. If the caller should know, return a boolean or error.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60bbf89 and 5a7d5cd.

⛔ Files ignored due to path filters (20)
  • apps/studio/public/tauri.svg is excluded by !**/*.svg
  • apps/studio/public/vite.svg is excluded by !**/*.svg
  • apps/studio/src-tauri/Cargo.lock is excluded by !**/*.lock
  • apps/studio/src-tauri/icons/128x128.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/128x128@2x.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/32x32.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square107x107Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square142x142Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square150x150Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square284x284Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square30x30Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square310x310Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square44x44Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square71x71Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/Square89x89Logo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/StoreLogo.png is excluded by !**/*.png
  • apps/studio/src-tauri/icons/icon.ico is excluded by !**/*.ico
  • apps/studio/src-tauri/icons/icon.png is excluded by !**/*.png
  • apps/studio/src/assets/react.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (89)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .gitignore
  • .vscode/extensions.json
  • .vscode/settings.json
  • README.md
  • apps/server/.gitignore
  • apps/server/README.md
  • apps/server/package.json
  • apps/server/src/code/extractors/javascript.ts
  • apps/server/src/code/extractors/typescript.ts
  • apps/server/src/code/index.ts
  • apps/server/src/code/parser.ts
  • apps/server/src/code/types.ts
  • apps/server/src/database/index.ts
  • apps/server/src/database/migrations/0001_initial.ts
  • apps/server/src/database/migrations/index.ts
  • apps/server/src/database/migrations/types.ts
  • apps/server/src/database/migrations/utils.ts
  • apps/server/src/database/schema.ts
  • apps/server/src/index.ts
  • apps/server/src/memory/embeddings.ts
  • apps/server/src/memory/index.ts
  • apps/server/src/memory/search.ts
  • apps/server/src/rules/index.ts
  • apps/server/src/server.ts
  • apps/server/tests/code/analyzer.test.ts
  • apps/server/tests/code/javascript.test.ts
  • apps/server/tests/code/parser.test.ts
  • apps/server/tests/code/typescript.test.ts
  • apps/server/tests/database/entities.test.ts
  • apps/server/tests/database/manager.test.ts
  • apps/server/tests/database/migrations.test.ts
  • apps/server/tests/database/vectors.test.ts
  • apps/server/tests/memory/embeddings.test.ts
  • apps/server/tests/memory/manager.test.ts
  • apps/server/tests/memory/search.test.ts
  • apps/server/tests/rules/engine.test.ts
  • apps/server/tests/server/tools.test.ts
  • apps/server/tests/setup.ts
  • apps/server/tsconfig.json
  • apps/studio/.gitignore
  • apps/studio/README.md
  • apps/studio/components.json
  • apps/studio/editor.html
  • apps/studio/index.html
  • apps/studio/package.json
  • apps/studio/src-tauri/.gitignore
  • apps/studio/src-tauri/Cargo.toml
  • apps/studio/src-tauri/build.rs
  • apps/studio/src-tauri/capabilities/default.json
  • apps/studio/src-tauri/icons/icon.icns
  • apps/studio/src-tauri/src/commands.rs
  • apps/studio/src-tauri/src/database.rs
  • apps/studio/src-tauri/src/lib.rs
  • apps/studio/src-tauri/src/main.rs
  • apps/studio/src-tauri/src/models.rs
  • apps/studio/src-tauri/src/store.rs
  • apps/studio/src-tauri/src/window.rs
  • apps/studio/src-tauri/tauri.conf.json
  • apps/studio/src/App.css
  • apps/studio/src/App.tsx
  • apps/studio/src/components/launcher/footer.tsx
  • apps/studio/src/components/launcher/index.ts
  • apps/studio/src/components/launcher/project-card.tsx
  • apps/studio/src/components/launcher/project-list.tsx
  • apps/studio/src/components/launcher/search-input.tsx
  • apps/studio/src/components/launcher/status-badge.tsx
  • apps/studio/src/components/ui/alert-dialog.tsx
  • apps/studio/src/components/ui/button.tsx
  • apps/studio/src/components/ui/input.tsx
  • apps/studio/src/components/ui/scroll-area.tsx
  • apps/studio/src/editor/Editor.tsx
  • apps/studio/src/editor/components/sidebar.tsx
  • apps/studio/src/editor/main.tsx
  • apps/studio/src/editor/pages/cognitive-logs.tsx
  • apps/studio/src/editor/pages/memory-graph.tsx
  • apps/studio/src/editor/pages/overview.tsx
  • apps/studio/src/editor/pages/rules-engine.tsx
  • apps/studio/src/lib/tauri.ts
  • apps/studio/src/lib/utils.ts
  • apps/studio/src/main.tsx
  • apps/studio/src/vite-env.d.ts
  • apps/studio/tsconfig.json
  • apps/studio/tsconfig.node.json
  • apps/studio/vite.config.ts
  • biome.json
  • package.json
  • turbo.json
🧰 Additional context used
🧬 Code graph analysis (14)
apps/studio/src/editor/components/sidebar.tsx (2)
apps/studio/src/editor/Editor.tsx (1)
  • TabId (8-12)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src-tauri/src/models.rs (1)
apps/studio/src/lib/tauri.ts (2)
  • StoredProject (3-7)
  • DatabaseStats (9-11)
apps/studio/src/components/ui/scroll-area.tsx (1)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src-tauri/src/database.rs (2)
apps/studio/src/lib/tauri.ts (1)
  • DatabaseStats (9-11)
apps/server/src/database/index.ts (1)
  • path (422-424)
apps/studio/src/components/launcher/project-list.tsx (1)
apps/studio/src/components/launcher/project-card.tsx (2)
  • Project (5-11)
  • ProjectCard (27-109)
apps/studio/src/components/ui/button.tsx (1)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src-tauri/src/store.rs (3)
apps/studio/src/lib/tauri.ts (1)
  • StoredProject (3-7)
apps/server/src/database/index.ts (1)
  • path (422-424)
apps/studio/src-tauri/src/commands.rs (2)
  • add_project (33-35)
  • remove_project (38-40)
apps/studio/src/components/launcher/status-badge.tsx (2)
apps/studio/src/components/launcher/index.ts (2)
  • ProjectStatus (5-5)
  • StatusBadge (5-5)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src/components/ui/input.tsx (1)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src/components/launcher/footer.tsx (1)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src-tauri/src/commands.rs (3)
apps/studio/src-tauri/src/database.rs (1)
  • get_stats (4-16)
apps/studio/src-tauri/src/store.rs (3)
  • get_all_projects (23-34)
  • add_project (36-69)
  • remove_project (71-92)
apps/studio/src-tauri/src/window.rs (1)
  • open_editor (6-76)
apps/studio/src/components/ui/alert-dialog.tsx (2)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src/components/ui/button.tsx (1)
  • buttonVariants (62-62)
apps/studio/src/components/launcher/search-input.tsx (3)
apps/studio/src/components/launcher/index.ts (1)
  • SearchInput (4-4)
apps/studio/src/lib/utils.ts (1)
  • cn (4-6)
apps/studio/src/components/ui/input.tsx (1)
  • Input (21-21)
apps/studio/src-tauri/src/lib.rs (2)
apps/studio/src-tauri/src/commands.rs (6)
  • add_project (33-35)
  • get_database_stats (23-25)
  • get_projects (28-30)
  • open_database_dialog (9-20)
  • open_editor_window (43-49)
  • remove_project (38-40)
apps/studio/src-tauri/src/store.rs (2)
  • add_project (36-69)
  • remove_project (71-92)
🪛 LanguageTool
apps/server/README.md

[style] ~366-~366: Consider using a less common alternative to make your writing sound more unique and professional.
Context: ...ontributing Contributions are welcome! Please feel free to submit a Pull Request. 1. Fork the rep...

(FEEL_FREE_TO_STYLE_ME)


[style] ~370-~370: Consider using a more formal and expressive alternative to ‘amazing’.
Context: ...azing-feature) 3. Commit your changes (git commit -m 'Add amazing feature') 4. Push to the branch (git ...

(AWESOME)

🪛 markdownlint-cli2 (0.18.1)
apps/server/README.md

6-6: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


64-64: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


221-221: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


290-290: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

README.md

39-39: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


53-53: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


67-67: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


81-81: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


100-100: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


116-116: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (66)
apps/studio/src/vite-env.d.ts (1)

1-1: LGTM! Standard Vite + TypeScript setup.

This triple-slash directive correctly includes Vite's client-side type definitions, enabling TypeScript support for Vite-specific features like import.meta.env and HMR APIs. This is standard boilerplate for Vite projects.

.vscode/extensions.json (1)

1-3: LGTM!

The recommended extensions align well with the Tauri/Rust infrastructure being added in the studio app. The rust-lang.rust-analyzer pairs correctly with the Rust-specific settings configured in settings.json.

biome.json (2)

8-13: LGTM!

The CSS parser configuration properly enables Tailwind directive support (@tailwind, @apply, @layer, etc.), which aligns with the Tailwind-driven UI components introduced in the studio app.


31-32: Acceptable configuration choices.

Disabling noNonNullAssertion allows TypeScript's ! operator, which is a reasonable tradeoff for developer convenience in projects with well-understood data flows. Disabling useImportType is fine since modern bundlers like Vite handle type-only imports efficiently regardless.

apps/studio/src/App.css (4)

1-4: LGTM!

The Tailwind CSS v4 import syntax and custom dark variant definition are correctly implemented. The class-based dark mode approach using @custom-variant dark (&:is(.dark *)) is appropriate for toggling themes programmatically.


6-46: LGTM!

The @theme inline block correctly maps CSS custom properties to Tailwind's theming system. The radius scale and color token mappings are comprehensive and well-structured for the design system.


48-82: LGTM!

The light theme uses OKLCH color space effectively. The neutral palette with zero chroma is appropriate, and the base radius value provides a consistent foundation for the radius scale.


147-154: LGTM!

These body styles are appropriate for a Tauri desktop application. Disabling overflow and user-select creates an app-like experience. Note that Tailwind v4's built-in Lightning CSS handles vendor prefixes automatically, so lines 150-152 may be redundant but don't cause issues.

.github/workflows/release.yml (1)

25-27: LGTM on scoped publish for monorepo.

The working-directory: apps/server correctly targets the server package within the monorepo structure. The step name update ("Publish server to npm") accurately reflects the scoped behavior.

If other packages in this monorepo need to be published to npm in the future, consider whether this workflow should be extended or if separate workflows are preferred.

.github/workflows/ci.yml (2)

10-11: Good rename to reflect job scope.

Renaming from test to ci better reflects the job's responsibilities (lint, build, and test).


30-31: Consistent test invocation across workflows.

Using bun run test instead of bun test ensures the test script defined in package.json is executed, which is more maintainable and aligns with the release workflow.

apps/studio/src-tauri/.gitignore (1)

1-7: LGTM!

Standard Tauri and Cargo ignore patterns are correctly configured. The /target/ directory for build artifacts and /gen/schemas for Tauri-generated files are appropriate exclusions.

apps/server/.gitignore (1)

1-34: LGTM!

Comprehensive and well-organized gitignore configuration for the server app. All critical patterns are covered including dependencies, build outputs, environment variables, logs, caches, and IDE artifacts.

apps/studio/README.md (1)

1-7: LGTM!

Standard Tauri template README with appropriate IDE setup recommendations. As the studio app evolves, consider expanding this with project-specific setup instructions, architecture overview, and development workflows.

apps/server/README.md (1)

1-382: Well-documented README!

Comprehensive documentation covering problem statement, solution, features, installation, API reference, workflow diagrams, and architecture. The structure is clear and provides good onboarding material for users of the code-recall server.

apps/studio/src/editor/pages/rules-engine.tsx (1)

1-15: LGTM!

Clean placeholder component following the same pattern as other editor pages. The component structure and Tailwind styling are consistent with the sibling page components.

apps/studio/src-tauri/build.rs (1)

1-3: LGTM!

Standard Tauri build script. This is the required boilerplate for Tauri projects to handle build-time resource processing.

apps/studio/src/editor/pages/overview.tsx (1)

1-15: LGTM!

Well-structured placeholder component consistent with the other editor page components. The component correctly uses React 19 conventions and follows the established pattern for editor pages.

apps/studio/src/editor/pages/cognitive-logs.tsx (1)

1-15: LGTM!

Clean placeholder component with proper structure. The centered layout with icon, heading, and subtitle follows a consistent pattern with other editor pages.

apps/studio/src/lib/utils.ts (1)

1-6: LGTM!

Standard cn utility for Tailwind class composition, following the shadcn/ui recommended pattern. Correctly combines clsx for conditional classes with twMerge for conflict resolution.

apps/studio/tsconfig.json (1)

1-30: LGTM!

Well-configured TypeScript setup for a Vite + React app:

  • Bundler mode resolution is correct for Vite
  • isolatedModules and noEmit are appropriate for bundler-driven builds
  • Strict mode with linting rules enforces good practices
  • Path alias @/* aligns with the shadcn/ui configuration in components.json
apps/studio/components.json (1)

1-22: LGTM!

shadcn/ui configuration is properly set up for this Tauri/Vite app:

  • "rsc": false is correct since this is not a Next.js app with React Server Components
  • Aliases align with the path mappings in tsconfig.json
  • Icon library set to lucide matches the imports in the editor pages

Note: The AI summary incorrectly states "server components flag (true)" but the actual code correctly sets "rsc": false.

turbo.json (1)

1-31: LGTM! Well-structured Turborepo configuration.

The task definitions follow Turborepo best practices:

  • Build task correctly declares dist/** outputs for caching
  • Test/lint tasks appropriately have empty outputs
  • dev task disables caching and enables persistent mode for watch functionality
  • Recursive ^check-types dependency ensures type-checking respects workspace dependencies
apps/server/package.json (3)

7-9: Verify bin entry works correctly for distribution.

Pointing bin directly to a TypeScript source file (src/index.ts) works with Bun but may cause issues if users attempt to run this package with Node.js. Consider pointing to the built output or adding a shebang with explicit bun invocation.


57-59: TypeScript peer dependency is correct.

TypeScript 5.9.3 is the latest stable release and is appropriate as a peer dependency.


45-53: Review sqlite-vec alpha dependency for stability and API compatibility.

sqlite-vec at 0.1.7-alpha.2 is an active alpha release and may have breaking changes or stability issues. Consider whether this project can tolerate alpha dependencies or if an alternative should be used.

Likely an incorrect or invalid review comment.

apps/studio/vite.config.ts (1)

16-23: Multi-entry build configuration looks correct.

The Rollup input configuration correctly sets up separate entry points for main and editor pages, which aligns with the PR's multi-window support for the Tauri application.

apps/studio/tsconfig.node.json (1)

1-15: Configuration is appropriate for Vite tooling.

The settings correctly configure TypeScript for the Vite config file with bundler module resolution and composite project support.

Note: The @/* path alias defined here may not be utilized since vite.config.ts typically doesn't import application source files. However, it maintains consistency with the main tsconfig and causes no harm.

apps/studio/src-tauri/Cargo.toml (1)

20-29: Tauri ecosystem dependencies are correctly configured.

The plugin versions (tauri-plugin-opener@2.5.3, tauri-plugin-dialog@2.5.0, tauri-plugin-store@2.4.2) are all compatible with tauri@2.9.5 and follow the v2 plugin line. The macos-private-api feature appropriately enables macOS-specific functionality for the multi-window support.

apps/studio/src-tauri/tauri.conf.json (2)

12-13: Verify macOS App Store compatibility.

Enabling macOSPrivateApi: true grants access to private macOS APIs for features like transparent windows or titlebar customization. Be aware this may affect Mac App Store approval if you plan to distribute through that channel.


1-11: LGTM!

The build configuration correctly uses Bun commands, and the launcher window setup (non-resizable, centered, specific dimensions) is appropriate for a project picker/launcher UI. The window label "launcher" aligns with the capabilities defined in default.json.

Also applies to: 14-22, 28-38

apps/studio/src-tauri/src/models.rs (1)

1-13: LGTM!

The Rust structs correctly mirror the TypeScript interfaces in apps/studio/src/lib/tauri.ts. The derive macros are appropriate: StoredProject includes Deserialize for bidirectional serialization, while DatabaseStats only needs Serialize as a response-only type.

apps/studio/src/components/launcher/footer.tsx (1)

1-23: LGTM!

The component structure is clean with proper semantic HTML, appropriate use of type="button" attributes, and good accessibility hints via <kbd> elements. The Tailwind classes follow v4 conventions correctly.

Also applies to: 34-75

apps/studio/src-tauri/capabilities/default.json (1)

1-13: LGTM!

The capability configuration follows the principle of least privilege, granting only the permissions necessary for the launcher and editor windows. The window labels correctly reference both the statically configured "launcher" window and the dynamically created "editor" window.

apps/studio/src/components/ui/scroll-area.tsx (2)

1-27: LGTM!

The ScrollArea component correctly wraps Radix UI primitives with proper data-slot attributes for styling hooks and accessibility-friendly focus-visible styles on the Viewport.

One minor note: the component always renders a vertical <ScrollBar /> (line 23). If horizontal scrolling is needed in the future, consumers would need to compose their own ScrollArea or this component could accept a scrollbar configuration prop.


29-56: LGTM!

The ScrollBar component properly handles both vertical and horizontal orientations with appropriate conditional styling. The use of cn() for class composition and spreading props allows good flexibility.

apps/studio/src/components/ui/input.tsx (1)

5-21: LGTM!

The Input component is well-structured with comprehensive styling covering:

  • Focus states with ring effects
  • File input styling
  • Accessibility via aria-invalid states
  • Dark mode support
  • Proper disabled states

The className composition correctly allows consumer overrides.

apps/studio/src/components/launcher/search-input.tsx (1)

13-39: LGTM with a minor observation.

The component is well-composed with proper icon positioning and focus state handling via the group class.

The CornerDownLeft indicator (lines 33-36) visually suggests "press Enter to submit," but there's no onKeyDown handler to capture that action. If this is intended as a hint for parent components to handle, that's fine. Otherwise, consider adding an optional onSubmit callback triggered on Enter key press.

apps/studio/src/components/ui/button.tsx (1)

7-37: LGTM!

The CVA configuration is well-structured with:

  • Comprehensive variant options covering common button styles
  • Icon-specific size variants (icon, icon-sm, icon-lg)
  • Proper focus-visible and aria-invalid accessibility states
  • Dark mode support
  • Modern Tailwind v4 selectors like has-[>svg]
apps/studio/src/components/launcher/status-badge.tsx (2)

11-42: LGTM!

The config-driven approach with statusConfig is clean and type-safe. Using Record<ProjectStatus, ...> ensures compile-time validation that all status variants are handled.


44-62: LGTM!

The component correctly renders status indicators with appropriate visual cues (pulse for active, spinner for sync). The badge is accessible since the status is communicated via visible label text.

Minor note: the pulse indicator (line 56) hardcodes bg-green-400 rather than deriving it from config, but this works since only "active" uses showPulse.

apps/studio/src/components/ui/alert-dialog.tsx (1)

1-154: Well-structured Radix UI wrapper components.

The AlertDialog component suite follows the established shadcn/ui pattern well, with proper composition, styling via cn utility, and consistent use of buttonVariants for action buttons.

apps/studio/src-tauri/src/main.rs (1)

1-6: LGTM!

Clean and minimal Tauri entry point. The Windows subsystem attribute correctly suppresses the console window in release builds, and delegating to studio_lib::run() keeps the entry point simple.

apps/studio/editor.html (1)

1-14: LGTM!

Standard HTML5 entry point for the editor application with appropriate dark theme class, viewport configuration, and module script loading.

apps/studio/src/main.tsx (1)

1-9: LGTM!

Standard React 18 entry point with StrictMode wrapper. The type assertion is acceptable here since the HTML template is controlled and guaranteed to have the root element.

apps/studio/src/editor/main.tsx (1)

1-10: LGTM!

Consistent React 18 entry point pattern for the Editor application, properly importing global styles and rendering within StrictMode.

apps/studio/index.html (1)

1-14: LGTM!

Clean HTML5 entry point for the main Studio application, consistent with the editor.html structure and properly configured for Vite's module loading.

apps/studio/src-tauri/src/lib.rs (2)

1-10: LGTM! Module structure and imports are well-organized.

The module declarations and command imports follow Tauri conventions. All six command handlers are correctly imported and will be registered in the invoke handler.


12-28: LGTM! Tauri application bootstrap is properly configured.

The plugin initialization order is correct, and all imported commands are registered via generate_handler!. The use of expect() for the run error is acceptable for a Tauri application entry point since there's no meaningful recovery from a failed app startup.

apps/studio/src/components/launcher/project-list.tsx (1)

1-8: LGTM! Clean interface definition with proper typing.

The ProjectListProps interface is well-structured with appropriate optional modifiers for callback props.

apps/studio/src/components/launcher/index.ts (1)

1-5: LGTM! Clean barrel exports with proper type-only annotations.

The index file provides a well-organized single entry point for all launcher components. Using type keyword for type-only exports ensures they're erased at compile time.

apps/studio/src/editor/Editor.tsx (3)

8-12: LGTM! Well-defined tab type.

The TabId union type provides type safety for tab navigation and is correctly exported for use by child components like Sidebar.


27-40: LGTM! Switch statement with defensive default case.

The renderContent function properly handles all TabId cases with a sensible fallback to Overview.


42-57: LGTM! Clean component layout with proper prop passing.

The Editor layout correctly passes state and handlers to Sidebar, and the main content area renders the active tab's content.

apps/studio/src/editor/components/sidebar.tsx (3)

1-12: LGTM! Imports are well-organized.

Clean separation of Tauri API, icons, utilities, and type imports.


14-26: LGTM! Props interface and navigation config are well-typed.

The SidebarProps interface provides clear typing, and the navItems array configuration is type-safe with TabId.


39-113: LGTM! Well-structured sidebar with clear visual hierarchy.

The component is well-organized with distinct sections for header, project info, switch button, and navigation. The conditional styling for active tabs using cn() is clean, and the Tailwind v4 gradient syntax (bg-linear-to-br) is correct.

apps/studio/src/components/launcher/project-card.tsx (2)

5-11: LGTM!

The Project interface is well-defined with appropriate types for all fields.


20-25: LGTM!

The formatVectorCount utility handles formatting correctly:

  • Values ≥10,000 show as whole numbers (e.g., "12k")
  • Values 1,000-9,999 show one decimal (e.g., "1.5k")
  • Values <1,000 use locale formatting
apps/studio/src/App.tsx (1)

178-197: LGTM!

The AlertDialog implementation for delete confirmation is well-structured with proper open state management and clear messaging about what the action does.

apps/studio/src-tauri/src/commands.rs (2)

27-40: LGTM!

The project CRUD commands properly delegate to the store module and use consistent error handling patterns.


42-49: LGTM!

The open_editor_window command cleanly delegates to the window module with proper parameter passing.

apps/studio/src/lib/tauri.ts (2)

3-11: LGTM!

The TypeScript interfaces correctly mirror the Rust StoredProject and DatabaseStats structs from models.rs.


13-31: LGTM!

The invoke wrappers are clean and properly typed. The functions provide a good abstraction layer between the React frontend and Tauri backend.

apps/studio/src-tauri/src/store.rs (2)

10-21: Path extraction assumes specific directory structure.

The function expects paths like /path/to/my-project/.code-recall/memory.db and navigates two levels up. If users select a database file with a different structure, "Unknown" is returned.

This is acceptable for the current use case but consider adding a comment documenting this assumption.


23-34: LGTM!

The get_all_projects function handles the store access cleanly with appropriate error handling and default fallback.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 22 to 27
- name: Run tests
run: bun test
run: bun run test

- name: Publish to npm
- name: Publish server to npm
working-directory: apps/server
run: bun publish --access public
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check if apps/server/package.json exists and inspect its scripts
if [ -f apps/server/package.json ]; then
  echo "=== apps/server/package.json scripts ===="
  cat apps/server/package.json | jq '.scripts'
else
  echo "apps/server/package.json not found"
fi

Repository: AbianS/code-recall

Length of output: 374


🏁 Script executed:

# Also check the full release.yml workflow to understand context
echo "=== .github/workflows/release.yml ===="
cat .github/workflows/release.yml

Repository: AbianS/code-recall

Length of output: 632


🏁 Script executed:

# Check if there's a root package.json with build scripts
echo "=== Root package.json scripts ===="
cat package.json 2>/dev/null | jq '.scripts' || echo "No root package.json or no scripts"

Repository: AbianS/code-recall

Length of output: 277


Add build step before publishing.

The apps/server/package.json has a build script that compiles TypeScript to the dist directory, but there are no prepublishOnly or prepack hooks to automatically trigger it. The release workflow currently runs tests and publishes without building, which means stale or missing compiled artifacts will be published to npm.

Add a build step before the publish step:

Suggested fix
      - name: Run tests
        run: bun run test

+     - name: Build
+       run: bun run build
+
      - name: Publish server to npm
        working-directory: apps/server
        run: bun publish --access public
📝 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
- name: Run tests
run: bun test
run: bun run test
- name: Publish to npm
- name: Publish server to npm
working-directory: apps/server
run: bun publish --access public
- name: Run tests
run: bun run test
- name: Build
run: bun run build
- name: Publish server to npm
working-directory: apps/server
run: bun publish --access public
🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 22 - 27, The release workflow
publishes apps/server without running its build, so add a build step before the
"Publish server to npm" job: insert a step named like "Build server" that runs
the server build command (e.g., run: bun run build) with working-directory:
apps/server, ensuring it runs after "Run tests" and before the publish step so
the dist artifacts are produced before bun publish; if the package.json build
script uses a different command name, call that script instead.

Comment on lines +8 to +20
#[tauri::command]
pub async fn open_database_dialog(app: AppHandle) -> Result<Option<String>, String> {
let file_path = app
.dialog()
.file()
.add_filter("SQLite Database", &["db", "sqlite"])
.blocking_pick_file();

match file_path {
Some(path) => Ok(Some(path.to_string())),
None => Ok(None),
}
}
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

🧩 Analysis chain

🌐 Web query:

Tauri dialog plugin blocking_pick_file async command runtime

💡 Result:

Short answer: blocking_pick_file is a synchronous, blocking API and must not be called on the async/main runtime thread. Use the non‑blocking pick_file callback, or run the blocking call on a blocking thread (e.g. tauri::async_runtime::spawn_blocking). See docs. [1][2][3]

Examples:

  • Use non‑blocking API (preferred for UI/main-thread):

    • app.dialog().file().pick_file(|path| { /* handle Option */ });. [1][2]
  • Run the blocking API off the async runtime:

    • tauri::async_runtime::spawn_blocking(move || app.dialog().file().blocking_pick_file())
      .await
      .unwrap();. [2][3]

References:

  • Tauri dialog plugin docs (pick_file / blocking_pick_file). [1][2]
  • tauri::async_runtime (spawn_blocking / runtime notes). [3]

🌐 Web query:

Tauri tauri_plugin_dialog async file picker API

💡 Result:

Short summary + examples.

  • JS (async): use @tauri-apps/plugin-dialog open/save which return a path/URI via await. Example:
    import { open } from '@tauri-apps/plugin-dialog';
    const file = await open({ multiple: false, directory: false }); // file = string | string[] | null. [1]

  • Rust (async/non-blocking): tauri_plugin_dialog exposes DialogExt with async/non-blocking variants (pick_file / pick_files / pick_file callback). Example (non-blocking callback shown; blocking variants also exist):
    use tauri_plugin_dialog::DialogExt;
    app.dialog().file().pick_file(|file_path| {
    // file_path: Option
    });
    (There are also blocking methods like blocking_pick_file and async-friendly patterns in the crate docs.) [2]

References

  • Tauri plugin dialog docs (JS usage, open/save examples). [1]
  • tauri_plugin_dialog crate docs (Rust DialogExt, pick_file / blocking_pick_file). [2]

References:
[1] Tauri — Dialog plugin (JS examples & API).
[2] docs.rs — tauri_plugin_dialog crate (Rust API).


🌐 Web query:

Tauri blocking operation in async command best practices

💡 Result:

Short best-practices for avoiding blocking operations in Tauri async commands:

  • Prefer async commands (async fn or #[tauri::command(async)]) so work runs on Tauri’s tokio-based async runtime. (v1.tauri.app)
  • Never run long blocking or CPU-bound code directly inside an async task — it can stall the runtime thread pool and UI. (jonaskruckenberg.github.io)
  • Offload blocking work with tokio::task::spawn_blocking (or tauri::async_runtime::spawn_blocking) for sync I/O or computations that can block. (docs.rs)
  • For heavy CPU-bound workloads consider a dedicated CPU executor (rayon) or limit concurrency with a semaphore to avoid saturating threads. (docs.rs)
  • Don’t use blocking_lock / other blocking primitives inside async contexts; instead use async locks or move blocking sections into spawn_blocking. (docs.rs)
  • If you need cancellation/abort semantics for long-running blocking tasks, implement cooperative cancellation (AtomicBool/cancellation token) because spawn_blocking tasks aren’t abortable once started. (docs.rs)
  • On shutdown be aware spawn_blocking tasks may keep running; consider shutdown_timeout or design tasks to finish/observe cancellation. (docs.rs)

If you want, I can show a minimal code example demonstrating spawn_blocking + a semaphore (Tokio/rayon) for a Tauri command.

Citations:


Use non-blocking dialog API or offload blocking call to separate thread.

blocking_pick_file() called in an async command will block the Tauri async runtime thread, stalling other commands and the UI.

Use either:

  1. The non-blocking callback variant: app.dialog().file().pick_file(|path| { /* handle path */ })
  2. Or wrap the blocking call: tauri::async_runtime::spawn_blocking(move || app.dialog().file().blocking_pick_file()).await
🤖 Prompt for AI Agents
In `@apps/studio/src-tauri/src/commands.rs` around lines 8 - 20,
open_database_dialog currently calls app.dialog().file().blocking_pick_file()
inside an async command which will block the Tauri async runtime; change it to
use the non-blocking API or offload the blocking call: either replace
blocking_pick_file() with the callback variant
app.dialog().file().pick_file(|path| { /* convert path to String and return via
a oneshot or emit event */ }) and adapt the function to return via the callback,
or wrap the blocking call using tauri::async_runtime::spawn_blocking(move ||
app.dialog().file().blocking_pick_file()).await and then convert the result to
Result<Option<String>, String> before returning from open_database_dialog.

Comment on lines +7 to +13
let memory_count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM memories WHERE archived = 0",
[],
|row| row.get(0),
)
.unwrap_or(0);
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

Inconsistent error handling: query failures are silently swallowed.

The unwrap_or(0) silently masks query errors (e.g., missing table, schema mismatch), while database connection errors are properly propagated. This inconsistency could lead to confusing behavior where memory_count is always 0 due to an undetected schema issue.

🔧 Proposed fix to propagate query errors consistently
     let memory_count: i64 = conn
         .query_row(
             "SELECT COUNT(*) FROM memories WHERE archived = 0",
             [],
             |row| row.get(0),
         )
-        .unwrap_or(0);
+        .map_err(|e| format!("Failed to query memory count: {}", e))?;

Alternatively, if returning 0 on missing table is intentional (e.g., for fresh databases), consider distinguishing between "table doesn't exist" and other errors, or add a comment documenting this behavior.

🤖 Prompt for AI Agents
In `@apps/studio/src-tauri/src/database.rs` around lines 7 - 13, The query for
memory_count is silently swallowing errors via unwrap_or(0) which masks schema
or other DB errors; replace the unwrap_or(0) with proper error propagation from
conn.query_row (e.g., return a Result from the surrounding function or use
map_err/? on the query), or explicitly match the query_row Result to return 0
only for a "no such table" / missing-table error and propagate all other errors;
update any function signatures that use memory_count (and references to
conn.query_row) to handle the propagated error accordingly and add a short
comment if returning 0 for absent tables is intentional.

Comment on lines +24 to +26
"security": {
"csp": null
}
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

Security risk: Content Security Policy is disabled.

Setting "csp": null disables the Content Security Policy entirely, which removes an important layer of protection against XSS and code injection attacks. Consider defining an appropriate CSP for your application.

🔒 Suggested CSP configuration
     "security": {
-      "csp": null
+      "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
     }

Adjust the policy based on your application's actual requirements (e.g., if you need to load external resources or use inline styles).

📝 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
"security": {
"csp": null
}
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
}
🤖 Prompt for AI Agents
In `@apps/studio/src-tauri/tauri.conf.json` around lines 24 - 26, The
configuration currently disables Content Security Policy by setting the
"security.csp" value to null; replace this with a restrictive, explicit CSP
string instead of null (update the "security" object key "csp") to re-enable XSS
and injection protections—define a baseline policy that restricts default-src to
the app itself, explicitly allow any external resources you require (e.g.,
scripts, styles, images, fonts) and only permit inline or eval features if
absolutely necessary, then test the app and iteratively relax directives for
required external origins or data: sources.

Comment on lines +118 to +129
/* Status colors */
--status-active: oklch(0.7 0.15 145);
--status-idle: oklch(0.68 0.015 255);
--status-sync: oklch(0.65 0.15 250);
--status-archived: oklch(0.5 0.01 255);
--status-offline: oklch(0.5 0.01 255);

/* Traffic light colors */
--traffic-red: #ff5f57;
--traffic-yellow: #febc2e;
--traffic-green: #28c840;
}
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

Status and traffic light colors are missing from light mode.

The --status-* and --traffic-* variables are only defined in the .dark block. If these variables are referenced in light mode, they will be undefined, potentially causing visual issues or falling back to unexpected values.

Consider adding these variables to the :root block with appropriate light-mode values:

💡 Suggested addition to :root

Add after line 81 in the :root block:

  /* Status colors */
  --status-active: oklch(0.55 0.15 145);
  --status-idle: oklch(0.5 0.01 255);
  --status-sync: oklch(0.5 0.15 250);
  --status-archived: oklch(0.4 0.01 255);
  --status-offline: oklch(0.4 0.01 255);

  /* Traffic light colors */
  --traffic-red: `#ff5f57`;
  --traffic-yellow: `#febc2e`;
  --traffic-green: `#28c840`;
🤖 Prompt for AI Agents
In `@apps/studio/src/App.css` around lines 118 - 129, The CSS defines --status-*
and --traffic-* variables only inside the .dark selector so they are undefined
in light mode; add matching variable definitions to the :root block (using
appropriate light-mode values) so --status-active, --status-idle, --status-sync,
--status-archived, --status-offline and
--traffic-red/--traffic-yellow/--traffic-green are available outside .dark and
will not fall back to unexpected values.

Comment on lines +40 to +107
return (
<button
type="button"
onClick={onClick}
className={cn(
"group grid grid-cols-12 items-center p-2.5 rounded-lg border transition-all duration-150 text-left w-full",
"focus:outline-none focus:ring-1 focus:ring-primary/40",
isSelected
? "bg-white/4 border-primary/40"
: "border-transparent hover:bg-white/3 hover:border-border",
)}
>
<div className="col-span-5 flex items-center gap-3 overflow-hidden">
<div
className={cn(
"w-9 h-9 rounded-lg border flex items-center justify-center shrink-0 shadow-sm transition-colors",
isActive
? "bg-linear-to-br from-primary/20 to-teal-900/20 border-primary/20 text-primary"
: "bg-secondary border-border text-muted-foreground group-hover:text-foreground",
)}
>
<Database className="w-5 h-5" />
</div>
<div className="min-w-0">
<span
className={cn(
"text-sm truncate block",
isActive
? "font-semibold text-foreground"
: "font-medium text-muted-foreground group-hover:text-foreground",
)}
>
{project.name}
</span>
<p className="text-[10px] text-muted-foreground/50 truncate font-mono">
{project.path}
</p>
</div>
</div>

<div className="col-span-2 text-right">
<span
className={cn(
"text-xs font-mono",
isActive ? "text-foreground" : "text-muted-foreground",
)}
>
{formatVectorCount(project.vectorCount)}
</span>
<span className="text-[9px] text-muted-foreground/50 block">
vectors
</span>
</div>

<div className="col-span-3 flex justify-end">
<StatusBadge status={project.status} />
</div>

<div className="col-span-2 flex justify-end">
<button
type="button"
onClick={handleDelete}
className="p-1.5 rounded-md opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</button>
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

Nested interactive elements: <button> inside <button> is invalid HTML.

The outer card is a <button> (line 41), but the delete action (line 99) is also a <button>. This violates HTML semantics, causes unpredictable behavior across browsers, and breaks accessibility (screen readers won't handle it correctly).

🔧 Proposed fix: Convert outer element to a div with role="button"
     return (
-    <button
-      type="button"
+    <div
+      role="button"
+      tabIndex={0}
       onClick={onClick}
+      onKeyDown={(e) => {
+        if (e.key === "Enter" || e.key === " ") {
+          e.preventDefault();
+          onClick?.();
+        }
+      }}
       className={cn(
         "group grid grid-cols-12 items-center p-2.5 rounded-lg border transition-all duration-150 text-left w-full",
         "focus:outline-none focus:ring-1 focus:ring-primary/40",
         isSelected
           ? "bg-white/4 border-primary/40"
           : "border-transparent hover:bg-white/3 hover:border-border",
       )}
     >
       {/* ... content ... */}
-    </button>
+    </div>
   );
📝 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
return (
<button
type="button"
onClick={onClick}
className={cn(
"group grid grid-cols-12 items-center p-2.5 rounded-lg border transition-all duration-150 text-left w-full",
"focus:outline-none focus:ring-1 focus:ring-primary/40",
isSelected
? "bg-white/4 border-primary/40"
: "border-transparent hover:bg-white/3 hover:border-border",
)}
>
<div className="col-span-5 flex items-center gap-3 overflow-hidden">
<div
className={cn(
"w-9 h-9 rounded-lg border flex items-center justify-center shrink-0 shadow-sm transition-colors",
isActive
? "bg-linear-to-br from-primary/20 to-teal-900/20 border-primary/20 text-primary"
: "bg-secondary border-border text-muted-foreground group-hover:text-foreground",
)}
>
<Database className="w-5 h-5" />
</div>
<div className="min-w-0">
<span
className={cn(
"text-sm truncate block",
isActive
? "font-semibold text-foreground"
: "font-medium text-muted-foreground group-hover:text-foreground",
)}
>
{project.name}
</span>
<p className="text-[10px] text-muted-foreground/50 truncate font-mono">
{project.path}
</p>
</div>
</div>
<div className="col-span-2 text-right">
<span
className={cn(
"text-xs font-mono",
isActive ? "text-foreground" : "text-muted-foreground",
)}
>
{formatVectorCount(project.vectorCount)}
</span>
<span className="text-[9px] text-muted-foreground/50 block">
vectors
</span>
</div>
<div className="col-span-3 flex justify-end">
<StatusBadge status={project.status} />
</div>
<div className="col-span-2 flex justify-end">
<button
type="button"
onClick={handleDelete}
className="p-1.5 rounded-md opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</button>
return (
<div
role="button"
tabIndex={0}
onClick={onClick}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onClick?.();
}
}}
className={cn(
"group grid grid-cols-12 items-center p-2.5 rounded-lg border transition-all duration-150 text-left w-full",
"focus:outline-none focus:ring-1 focus:ring-primary/40",
isSelected
? "bg-white/4 border-primary/40"
: "border-transparent hover:bg-white/3 hover:border-border",
)}
>
<div className="col-span-5 flex items-center gap-3 overflow-hidden">
<div
className={cn(
"w-9 h-9 rounded-lg border flex items-center justify-center shrink-0 shadow-sm transition-colors",
isActive
? "bg-linear-to-br from-primary/20 to-teal-900/20 border-primary/20 text-primary"
: "bg-secondary border-border text-muted-foreground group-hover:text-foreground",
)}
>
<Database className="w-5 h-5" />
</div>
<div className="min-w-0">
<span
className={cn(
"text-sm truncate block",
isActive
? "font-semibold text-foreground"
: "font-medium text-muted-foreground group-hover:text-foreground",
)}
>
{project.name}
</span>
<p className="text-[10px] text-muted-foreground/50 truncate font-mono">
{project.path}
</p>
</div>
</div>
<div className="col-span-2 text-right">
<span
className={cn(
"text-xs font-mono",
isActive ? "text-foreground" : "text-muted-foreground",
)}
>
{formatVectorCount(project.vectorCount)}
</span>
<span className="text-[9px] text-muted-foreground/50 block">
vectors
</span>
</div>
<div className="col-span-3 flex justify-end">
<StatusBadge status={project.status} />
</div>
<div className="col-span-2 flex justify-end">
<button
type="button"
onClick={handleDelete}
className="p-1.5 rounded-md opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
);

Comment on lines +34 to +37
const handleSwitchProject = async () => {
const window = getCurrentWindow();
await window.close();
};
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

Add error handling for window close operation.

The window.close() call could fail (e.g., if the window is already closing or permissions are restricted). Since this is an async function, unhandled rejections would silently fail.

🛡️ Proposed fix with error handling
   const handleSwitchProject = async () => {
-    const window = getCurrentWindow();
-    await window.close();
+    try {
+      const window = getCurrentWindow();
+      await window.close();
+    } catch (error) {
+      console.error("Failed to close window:", error);
+    }
   };
📝 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
const handleSwitchProject = async () => {
const window = getCurrentWindow();
await window.close();
};
const handleSwitchProject = async () => {
try {
const window = getCurrentWindow();
await window.close();
} catch (error) {
console.error("Failed to close window:", error);
}
};
🤖 Prompt for AI Agents
In `@apps/studio/src/editor/components/sidebar.tsx` around lines 34 - 37, The
handleSwitchProject function currently awaits window.close() without error
handling; wrap the async close call in a try/catch inside handleSwitchProject,
check that getCurrentWindow() returned a valid window before calling close
(e.g., guard with if (window)), await window.close() inside the try, and log or
surface any caught error (using existing logger or console.error) to avoid
unhandled promise rejections.

Comment on lines +33 to +41
export async function openEditorWindow(
projectName: string,
projectPath: string,
): Promise<void> {
return invoke<void>("open_editor_window", {
projectName,
projectPath,
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "pub fn open_editor_window" apps/studio/src-tauri/src/commands.rs -A 5

Repository: AbianS/code-recall

Length of output: 264


🏁 Script executed:

rg -n "open_editor_window" apps/studio/src-tauri/src/commands.rs -B 5 -A 10

Repository: AbianS/code-recall

Length of output: 422


🏁 Script executed:

cat apps/studio/src-tauri/Cargo.toml | head -40

Repository: AbianS/code-recall

Length of output: 1010


🏁 Script executed:

rg "open_editor_window" apps/studio/src --type ts --type tsx -B 2 -A 2

Repository: AbianS/code-recall

Length of output: 89


🏁 Script executed:

rg "open_editor_window" apps/studio/src -B 2 -A 2

Repository: AbianS/code-recall

Length of output: 328


🏁 Script executed:

rg -n "serde" apps/studio/src-tauri/src/commands.rs -B 2 -A 2

Repository: AbianS/code-recall

Length of output: 44


🏁 Script executed:

rg "invoke.*open_editor_window" apps/studio -r

Repository: AbianS/code-recall

Length of output: 475


🏁 Script executed:

rg "#\[tauri::command\]" apps/studio/src-tauri/src/commands.rs -A 3

Repository: AbianS/code-recall

Length of output: 857


Fix parameter naming mismatch: TypeScript sends camelCase but Rust expects snake_case.

The TypeScript invocation passes projectName and projectPath while the Rust command expects project_name and project_path. Tauri's #[tauri::command] macro does not automatically convert camelCase to snake_case—parameter names must match exactly. This will cause a runtime error when the command is invoked.

Change the TypeScript invoke to use snake_case parameter names:

return invoke<void>("open_editor_window", {
  project_name: projectName,
  project_path: projectPath,
});

Or update the Rust function parameters to use camelCase (less idiomatic).

🤖 Prompt for AI Agents
In `@apps/studio/src/lib/tauri.ts` around lines 33 - 41, The invoke call in
openEditorWindow uses camelCase keys (projectName, projectPath) but the Rust
tauri command expects snake_case (project_name, project_path); update the invoke
payload in openEditorWindow to send project_name mapped to projectName and
project_path mapped to projectPath so the parameter names match Rust, or
alternatively rename the Rust command parameters to camelCase to match the
current TS keys (preferred: change the invoke keys to snake_case).

Comment on lines +2 to +22
"name": "code-recall-monorepo",
"private": true,
"scripts": {
"start": "bun run src/index.ts",
"dev": "bun --watch run src/index.ts",
"build": "bun build src/index.ts --outdir dist --target bun",
"test": "bun test",
"lint": "biome check src tests",
"lint:fix": "biome check --write src tests"
},
"dependencies": {
"@modelcontextprotocol/sdk": "1.25.2",
"@xenova/transformers": "2.17.2",
"sqlite-vec": "0.1.7-alpha.2",
"tree-sitter-javascript": "0.25.0",
"tree-sitter-typescript": "0.23.2",
"web-tree-sitter": "0.26.3",
"zod": "3.25.76"
"build": "bun --bun turbo run build",
"dev": "bun --bun turbo run dev",
"test": "bun --bun turbo run test",
"lint": "turbo run lint",
"lint:fix": "turbo run lint:fix"
},
"devDependencies": {
"@biomejs/biome": "2.3.11",
"@types/bun": "1.2.15"
"turbo": "2.7.4",
"typescript": "5.9.3"
},
"engines": {
"bun": ">=1.0.0"
},
"peerDependencies": {
"typescript": "5"
}
"packageManager": "bun@1.3.5",
"workspaces": [
"apps/*"
]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

latest version of turbo npm package 2025

💡 Result:

As of January 14, 2026, the latest npm version of the turbo package is 2.5.6. [1]

Sources:
[1] npm: turbo package page — https://www.npmjs.com/package/turbo.


🌐 Web query:

latest version of @biomejs/biome npm

💡 Result:

The latest npm version of @biomejs/biome is 2.2.3. [1][2]

Sources:

  • npm package page for @biomejs/biome. [1]
  • Biome official website / repository. [2]

🌐 Web query:

latest version of bun package manager 2025

💡 Result:

The latest Bun release in 2025 was Bun v1.3.5 (released December 17, 2025). [1][2]

Sources:
[1] Bun 1.3 (overview, Oct 10, 2025).
[2] Bun v1.3.5 release notes (Dec 17, 2025).


Correct dependency versions in package.json.

The specified versions for turbo (2.7.4) and @biomejs/biome (2.3.11) do not exist in npm registries. As of January 2026, the latest available versions are turbo 2.5.6 and @biomejs/biome 2.2.3. Update these to valid versions.

Additionally, packageManager specifies bun@1.3.5 while engines permits >= 1.0.0. Either constrain the engines field to >= 1.3.5 to match the intended package manager version, or document the reason for supporting older Bun versions.

🤖 Prompt for AI Agents
In `@package.json` around lines 2 - 22, Update package.json to use valid
dependency versions and align the Bun constraint: replace the non-existent
"turbo": "2.7.4" with "turbo": "2.5.6" and "@biomejs/biome": "2.3.11" with
"@biomejs/biome": "2.2.3" in devDependencies, and then make engines.bun
consistent with packageManager by changing engines.bun to ">=1.3.5" (or
alternatively update packageManager to the broader version you intend to support
and document why); ensure the changes are only made in the devDependencies and
engines/packageManager fields so package resolution and engine enforcement
match.

Comment on lines +1 to +135
# Turborepo starter

<img width="1536" height="1024" alt="portada" src="https://github.com/user-attachments/assets/04e1f95a-1239-42b1-bd23-c4bbe273452b" />
This Turborepo starter is maintained by the Turborepo core team.

## Using this example

**Semantic memory for AI coding agents**
Run the following command:

Give your AI assistant persistent memory that learns from your project's history.

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Bun](https://img.shields.io/badge/Bun-1.0+-black?logo=bun)](https://bun.sh)
[![MCP](https://img.shields.io/badge/MCP-Compatible-green)](https://modelcontextprotocol.io)

[Features](#features)[Installation](#installation)[Quick Start](#quick-start)[Tools](#mcp-tools)[Best Practices](#best-practices)[Architecture](#architecture)

</div>

---

## The Problem

AI coding assistants are incredibly powerful, but they have a critical limitation: **they forget everything between sessions**. Every time you start a new conversation, your AI assistant has no memory of:

- Past architectural decisions and *why* they were made
- Patterns that work well in your codebase
- Approaches that failed and should be avoided
- Project-specific rules and guardrails

You end up repeating context, re-explaining decisions, and sometimes watching the AI make the same mistakes twice.
```sh
npx create-turbo@latest
```

### Why not just use CLAUDE.md?
## What's inside?

The typical solution is adding context to `.md` files. **It doesn't scale:**
This Turborepo includes the following packages/apps:

- Context window has limits — your instructions compete with actual code
- Loads everything even when you only need part of it
- No semantic search ("state management" won't find "Zustand")
- Can't learn from failures or evolve automatically
### Apps and Packages

## The Solution
- `docs`: a [Next.js](https://nextjs.org/) app
- `web`: another [Next.js](https://nextjs.org/) app
- `@repo/ui`: a stub React component library shared by both `web` and `docs` applications
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo

**code-recall** is an [MCP server](https://modelcontextprotocol.io) that gives AI agents persistent, semantic memory. It stores observations, decisions, and learnings in a local SQLite database with vector search capabilities, allowing your AI to:
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).

- **Remember** past decisions and their outcomes
- **Learn** from failures (failed approaches are boosted in future searches)
- **Follow rules** with semantic matching guardrails
- **Understand** your code structure through tree-sitter analysis
### Utilities

All data stays local on your machine. No cloud, no telemetry, fully private.
This Turborepo has some additional tools already setup for you:

---
- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [ESLint](https://eslint.org/) for code linting
- [Prettier](https://prettier.io) for code formatting

## Features
### Build

### Semantic Memory
Store and retrieve observations using hybrid search that combines:
- **Vector similarity** (embeddings) - finds conceptually related memories
- **Full-text search** (BM25) - matches exact keywords
- **Temporal decay** - recent memories rank higher
- **Failure boost** - past failures surface prominently to prevent repeating mistakes
To build all apps and packages, run the following command:

### Rules Engine
Create guardrails that are matched semantically against actions:
```
Trigger: "adding API endpoint"
→ Must do: ["Add rate limiting", "Write integration tests"]
→ Must not: ["Skip authentication"]
→ Ask first: ["Is this a breaking change?"]
```
cd my-turborepo
### Code Analysis
Parse TypeScript/JavaScript files with tree-sitter to extract:
- Classes, methods, and functions
- Interfaces and type aliases
- Import statements
- JSDoc documentation
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo build
Link memories to specific code entities for contextual recall.

---

## Installation

### Prerequisites

- **[Bun](https://bun.sh)** v1.0 or higher
- **macOS**: SQLite with extension support (via Homebrew: `brew install sqlite`)
- **Linux**: Works out of the box

### Install from npm

```bash
bun install -g code-recall
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo build
yarn dlx turbo build
pnpm exec turbo build
```

---

## Quick Start
You can build a specific package by using a [filter](https://turborepo.dev/docs/crafting-your-repository/running-tasks#using-filters):

### 1. Add to Claude Code

```bash
claude mcp add code-recall -- bunx code-recall
```
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo build --filter=docs
### 2. Restart Claude Code

The server starts automatically when you open Claude Code.

### 3. Start Using Memory

Once connected, Claude can store observations, search memories, check rules, and more using the [MCP tools](#mcp-tools) below.

### Configuration with Other MCP Clients

Add to your MCP client configuration:

```json
{
"mcpServers": {
"code-recall": {
"command": "bunx",
"args": ["code-recall"]
}
}
}
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo build --filter=docs
yarn exec turbo build --filter=docs
pnpm exec turbo build --filter=docs
```

---

## MCP Tools

code-recall provides 8 tools that AI agents can use:

### `store_observation`

Store a new observation in memory with automatic conflict detection.
### Develop

| Parameter | Type | Description |
|-----------|------|-------------|
| `category` | `decision` \| `pattern` \| `warning` \| `learning` | Type of observation |
| `content` | `string` | The main content |
| `rationale` | `string?` | Why this decision was made |
| `tags` | `string[]?` | Tags for categorization |
| `file_path` | `string?` | Associated file path |
To develop all apps and packages, run the following command:

```json
{
"category": "decision",
"content": "Use JWT for authentication",
"rationale": "Stateless auth enables horizontal scaling",
"tags": ["auth", "architecture"]
}
```
cd my-turborepo
### `search_memory`

Search memories using semantic similarity.

| Parameter | Type | Description |
|-----------|------|-------------|
| `query` | `string` | Search query |
| `limit` | `number?` | Max results (default: 10) |
| `category` | `string?` | Filter by category |
| `file_path` | `string?` | Filter by file path |

### `get_briefing`

Get a session start briefing with stats, warnings, and failed approaches.

| Parameter | Type | Description |
|-----------|------|-------------|
| `focus_areas` | `string[]?` | Topics to pre-fetch context for |

### `set_rule`

Create a new rule/guardrail with semantic trigger matching.

| Parameter | Type | Description |
|-----------|------|-------------|
| `trigger` | `string` | Action pattern that triggers this rule |
| `must_do` | `string[]?` | Required actions |
| `must_not` | `string[]?` | Forbidden actions |
| `ask_first` | `string[]?` | Questions to consider |

### `check_rules`

Check which rules apply to an action.

| Parameter | Type | Description |
|-----------|------|-------------|
| `action` | `string` | The action you're about to take |

### `record_outcome`

Record whether a decision worked or failed.

| Parameter | Type | Description |
|-----------|------|-------------|
| `memory_id` | `number` | ID of the memory |
| `outcome` | `string` | Description of what happened |
| `worked` | `boolean` | Whether it was successful |

### `list_rules`

List all active rules. No parameters.

### `analyze_structure`

Analyze a source file and extract its code structure.

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `string` | Path to the file |
| `include_types` | `string[]?` | Filter entity types |

---

## Recommended Workflow
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo dev
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo dev
yarn exec turbo dev
pnpm exec turbo dev
```
┌─────────────────────────────────────────────────────────┐
│ SESSION START │
│ │ │
│ ▼ │
│ get_briefing(focus_areas) │
│ │ │
└─────────────────────────┼───────────────────────────────┘
┌─────────────────────────┼───────────────────────────────┐
│ BEFORE CHANGES │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ check_rules(action) search_memory(query) │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────┼───────────────────────────────┐
│ AFTER DECISIONS │
│ │ │
│ ▼ │
│ store_observation(category, content) │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────┼───────────────────────────────┐
│ AFTER IMPLEMENTATION │
│ │ │
│ ▼ │
│ record_outcome(memory_id, worked) │
│ │
└─────────────────────────────────────────────────────────┘
```

---

## Best Practices
You can develop a specific package by using a [filter](https://turborepo.dev/docs/crafting-your-repository/running-tasks#using-filters):

### Claude Code Skill

For optimal integration with Claude Code, add our Skill to your project. [Skills](https://code.claude.com/docs/en/skills) teach Claude when and how to use code-recall automatically.

**Install the Skill:**

```bash
# From your project root
mkdir -p .claude/skills/code-recall
curl -o .claude/skills/code-recall/SKILL.md \
https://raw.githubusercontent.com/AbianS/code-recall/main/.claude/skills/code-recall/SKILL.md
```
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo dev --filter=web
Or create `.claude/skills/code-recall/SKILL.md` manually with [our skill template](https://github.com/AbianS/code-recall/blob/main/.claude/skills/code-recall/SKILL.md).

### What the Skill Does

Once installed, Claude will automatically:

- **Session start**: Call `get_briefing` to load context and warnings
- **Before changes**: Check `search_memory` and `check_rules` for relevant past decisions
- **After decisions**: Store important choices with `store_observation`
- **After implementation**: Record outcomes with `record_outcome`

This creates a feedback loop where Claude learns from past successes and failures in your project.

---

## Architecture

```
code-recall/
├── src/
│ ├── index.ts # Entry point
│ ├── server.ts # MCP server and tools
│ ├── database/
│ │ ├── index.ts # DatabaseManager (SQLite + sqlite-vec)
│ │ └── schema.ts # Database schema
│ ├── memory/
│ │ ├── index.ts # MemoryManager
│ │ ├── embeddings.ts # Local embeddings (@xenova/transformers)
│ │ └── search.ts # Hybrid search implementation
│ ├── rules/
│ │ └── index.ts # RulesEngine with semantic matching
│ └── code/
│ ├── index.ts # Code analysis coordinator
│ ├── parser.ts # tree-sitter WASM parser
│ └── extractors/ # Language-specific extractors
├── tests/ # Comprehensive test suite
└── .code-recall/ # Data directory (auto-created)
└── memory.db # SQLite database
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo dev --filter=web
yarn exec turbo dev --filter=web
pnpm exec turbo dev --filter=web
```

---

## Technical Stack

| Component | Technology |
|-----------|------------|
| **Runtime** | [Bun](https://bun.sh) |
| **Database** | SQLite + [sqlite-vec](https://github.com/asg017/sqlite-vec) |
| **Embeddings** | [@xenova/transformers](https://huggingface.co/docs/transformers.js) (all-MiniLM-L6-v2, 384d) |
| **Full-text Search** | SQLite FTS5 |
| **Code Parsing** | [web-tree-sitter](https://github.com/nickelproject/nickel/blob/main/packages/tree-sitter/README.md) |
| **Protocol** | [MCP](https://modelcontextprotocol.io) (stdio transport) |
### Remote Caching

### Search Algorithm
> [!TIP]
> Vercel Remote Cache is free for all plans. Get started today at [vercel.com](https://vercel.com/signup?/signup?utm_source=remote-cache-sdk&utm_campaign=free_remote_cache).
The hybrid search combines multiple signals:
Turborepo can use a technique known as [Remote Caching](https://turborepo.dev/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.

| Signal | Weight | Description |
|--------|--------|-------------|
| Vector similarity | 50% | Cosine similarity of embeddings |
| Full-text score | 30% | BM25 ranking |
| Recency | 15% | Exponential time decay |
| Failure boost | 5% | Failed decisions rank higher |
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup?utm_source=turborepo-examples), then enter the following commands:

---

## Development

```bash
# Run in development mode (with watch)
bun run dev

# Run tests
bun test

# Run specific test file
bun test tests/memory/search.test.ts
```
cd my-turborepo
### Test Coverage

The project includes 210+ tests covering:
- Database operations and vector search
- Embedding generation and similarity
- Hybrid search algorithm
- Rules engine semantic matching
- Code analysis and extraction
- MCP server tools
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo login
---

## Contributing
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo login
yarn exec turbo login
pnpm exec turbo login
```

Contributions are welcome! Please feel free to submit a Pull Request.
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:

---
```
# With [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation) installed (recommended)
turbo link
## License
# Without [global `turbo`](https://turborepo.dev/docs/getting-started/installation#global-installation), use your package manager
npx turbo link
yarn exec turbo link
pnpm exec turbo link
```

MIT License - see [LICENSE](LICENSE) for details.
## Useful Links

---
Learn more about the power of Turborepo:

<p align="center" style="text-align:center">with 💖 by <a href="https://github.com/AbianS" target="_blank">AbianS</a></p>
- [Tasks](https://turborepo.dev/docs/crafting-your-repository/running-tasks)
- [Caching](https://turborepo.dev/docs/crafting-your-repository/caching)
- [Remote Caching](https://turborepo.dev/docs/core-concepts/remote-caching)
- [Filtering](https://turborepo.dev/docs/crafting-your-repository/running-tasks#using-filters)
- [Configuration Options](https://turborepo.dev/docs/reference/configuration)
- [CLI Usage](https://turborepo.dev/docs/reference/command-line-reference)
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

README content doesn't match actual project structure.

This appears to be a default Turborepo template that doesn't reflect the actual monorepo contents:

  1. Inaccurate package references: Lines 19-23 describe docs, web, @repo/ui, @repo/eslint-config, and @repo/typescript-config, but the actual PR introduces apps/studio and apps/server.

  2. Incorrect tooling description: Lines 31-33 mention ESLint and Prettier, but the root package.json uses Biome for linting.

  3. Generic directory references: Commands reference cd my-turborepo (Lines 40, 68, 101) instead of the actual project name.

Consider updating the README to document the actual project structure, apps, and tooling.

Additionally, static analysis flagged that code blocks at Lines 39, 53, 67, 81, 100, and 116 are missing language specifiers. Adding sh or bash would improve rendering.

Example fix for Line 39
-```
+```sh
 cd my-turborepo
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

39-39: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


53-53: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


67-67: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


81-81: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


100-100: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


116-116: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@README.md` around lines 1 - 135, The README.md is a stale Turborepo template
that doesn't reflect this repo: update sections describing packages/apps to list
the actual apps (`apps/studio`, `apps/server`) and remove or replace references
to `docs`, `web`, `@repo/*` packages; update the tooling section to mention
Biome as used in the root package.json instead of ESLint/Prettier; replace
generic "cd my-turborepo" examples with the appropriate project name or remove
the redundant cd lines; and add language specifiers (e.g., sh or bash) to the
fenced code blocks flagged (around the build/dev/usage examples) so syntax
highlighting renders correctly.

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