Skip to content

feat(Beads): implement full Kanban board UI for issue tracking#5

Merged
0xtsotsi merged 13 commits intomainfrom
change_logo
Dec 24, 2025
Merged

feat(Beads): implement full Kanban board UI for issue tracking#5
0xtsotsi merged 13 commits intomainfrom
change_logo

Conversation

@0xtsotsi
Copy link
Owner

@0xtsotsi 0xtsotsi commented Dec 24, 2025

Summary

This PR implements a complete Kanban board interface for the Beads issue tracking system, providing a visual way to manage issues within DevFlow.

Frontend Components

  • beads-view.tsx: Main entry point with complete UI implementation
  • BeadsHeader: Header with stats, search, and add issue button
  • BeadsKanbanBoard: Drag-and-drop Kanban board with columns (Backlog, Ready, In Progress, Closed)
  • BeadsCard: Individual issue cards with metadata and actions
  • BeadsColumn: Column components with issue count and drop zone
  • BeadsBadges: Status and type badges with color coding

Custom Hooks

  • useBeadsIssues: Issue fetching and loading state
  • useBeadsColumnIssues: Column filtering and statistics
  • useBeadsActions: CRUD operations for issues
  • useBeadsDragDrop: Drag-and-drop functionality

Dialogs

  • CreateIssueDialog: Form for creating new issues
  • EditIssueDialog: Form for editing existing issues
  • DeleteIssueDialog: Confirmation dialog with blocking count warning

API Integration

  • Added BeadsAPI types to electron.ts
  • Implemented HTTP API client methods for all Beads operations
  • Connected frontend to backend endpoints

Documentation & Tooling

  • Added Beads UI integration section to README.md
  • Added npm scripts for beads-ui operations (beads, beads:open, beads:stop, etc.)
  • Added .env.example file for configuration

Test plan

  • Test creating a new issue via the Create Issue dialog
  • Test editing an existing issue
  • Test deleting an issue (with and without blocking issues)
  • Test dragging and dropping issues between columns
  • Test search/filter functionality
  • Test that status changes (start/close buttons) work correctly
  • Verify dependency tracking displays correct blocking/blocked counts
  • Test loading and error states
  • Verify responsive design and theme support
  • Test that the npm scripts for beads-ui work correctly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Keyboard shortcut for Beads added (default: B).
    • Board background settings per project (opacity, glassmorphism, scrollbar, borders).
    • Initialization validation with user-facing error when Beads isn’t set up.
  • Bug Fixes / Improvements

    • Safer drag-and-drop handling for Kanban columns (more stable item mapping).
    • Read operations return sensible defaults for uninitialized projects instead of errors.

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

0xtsotsi and others added 2 commits December 24, 2025 12:15
This PR implements a complete Kanban board interface for the Beads issue tracking system, providing a visual way to manage issues within DevFlow.

## Changes

### Frontend Components
- **beads-view.tsx**: Main entry point with complete UI implementation
- **BeadsHeader**: Header with stats, search, and add issue button
- **BeadsKanbanBoard**: Drag-and-drop Kanban board with columns (Backlog, Ready, In Progress, Closed)
- **BeadsCard**: Individual issue cards with metadata and actions
- **BeadsColumn**: Column components with issue count and drop zone
- **BeadsBadges**: Status and type badges with color coding

### Custom Hooks
- **useBeadsIssues**: Issue fetching and loading state
- **useBeadsColumnIssues**: Column filtering and statistics
- **useBeadsActions**: CRUD operations for issues
- **useBeadsDragDrop**: Drag-and-drop functionality

### Dialogs
- **CreateIssueDialog**: Form for creating new issues
- **EditIssueDialog**: Form for editing existing issues
- **DeleteIssueDialog**: Confirmation dialog with blocking count warning

### API Integration
- Added BeadsAPI types to electron.ts
- Implemented HTTP API client methods for all Beads operations
- Connected frontend to backend endpoints

### Documentation & Tooling
- Added Beads UI integration section to README.md
- Added npm scripts for beads-ui operations (beads, beads:open, beads:stop, etc.)
- Added .env.example file for configuration

## Features
- ✅ Visual Kanban board with 4 columns
- ✅ Drag-and-drop issue management
- ✅ Create, edit, and delete issues
- ✅ Search and filter issues
- ✅ Issue status tracking (Backlog → Ready → In Progress → Closed)
- ✅ Dependency tracking (blocking/blocked counts)
- ✅ Responsive design with theme support
- ✅ Loading and error states
- ✅ Toast notifications for user feedback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

coderabbitai bot commented Dec 24, 2025

Warning

Rate limit exceeded

@0xtsotsi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 51 seconds before requesting another review.

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d19aa8 and 2ca646e.

📒 Files selected for processing (5)
  • apps/server/src/routes/beads/client/cli-wrapper.ts
  • apps/server/src/routes/beads/common.ts
  • apps/server/src/services/beads-service.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/store/app-store.ts

Note

Other AI code review bot(s) detected

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

📝 Walkthrough

Walkthrough

Adds a bd binary lookup utility, converts bd invocations to execFile, introduces one new route handler factory (createCreateHandler), makes CLI wrapper functions async to await bd resolution, and scatters JSDoc and small UI prop/type/import adjustments across beads-related frontend components.

Changes

Cohort / File(s) Summary
Backend: Beads common util
apps/server/src/routes/beads/common.ts
Adds getBdBin(): Promise<string> (runs which bd), a promisified execFile wrapper, and keeps getErrorMessage/logError utilities.
Backend: CLI wrapper
apps/server/src/routes/beads/client/cli-wrapper.ts
runBd and runBdExec now async and await getBdBin() before spawning/executing bd.
Backend: Beads service execution changes
apps/server/src/services/beads-service.ts
Replace string-based exec calls with execFile + arg arrays; use cwd per project; adjust JSON parsing and ensure absent versions map to undefined; read ops return defaults when not initialized.
Backend: Routes — new handler
apps/server/src/routes/beads/routes/create.ts
New exported createCreateHandler(beadsService) that validates request and calls beadsService.createIssue, returns structured JSON and uses logError/getErrorMessage.
Backend: Routes — docs and JSDoc
apps/server/src/routes/beads/index.ts, apps/server/src/routes/beads/routes/{delete,list,ready,update,validate}.ts
Added/extended JSDoc blocks above router and route factories; no signature or runtime changes.
Frontend: Beads view & imports
apps/ui/src/components/views/beads-view.tsx, .../beads-view/*
Reworked import paths to nested beads-view structure; added initialization validation before creating issues; removed duplicate search state; preserved core UI behavior.
Frontend: Kanban board / column props
apps/ui/src/components/views/beads-view/beads-kanban-board.tsx, .../components/beads-column.tsx
Added board background settings usage; BeadsColumn gains new prop itemIds?: string[] (default []), and SortableContext now uses itemIds.
Frontend: Hooks & types
apps/ui/src/components/views/beads-view/hooks/*
Added JSDoc across many hooks; exported new BeadsStats interface in use-beads-column-issues.ts. No runtime signature changes except added exported type.
Frontend: UI docs & shortcuts
apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts, apps/ui/src/components/ui/keyboard-map.tsx, apps/ui/src/store/app-store.ts
JSDoc added; keyboard shortcut label/category extended with "Beads" and default shortcut beads: 'B' added to store.
Frontend: Dialogs & badges docs
apps/ui/src/components/views/beads-view/dialogs/*.tsx, .../components/beads-badges.tsx
JSDoc added above dialog and badge components; no behavior/signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant UI as BeadsView (React)
    participant API as Electron API (IPC)
    participant Route as Server Route (Express)
    participant Service as BeadsService
    participant CLI as bd (Beads CLI)

    rect rgb(200,230,201)
    note right of UI: Create Issue (updated to await bd lookup)
    UI->>API: api.beads.create(projectPath, issue)
    API->>Route: POST /beads/create
    Route->>Service: createIssue(projectPath, issue)
    Service->>Service: build execFile args
    Service->>CLI: spawn execFile("bd", ["issue","create",...], {cwd})
    CLI-->>Service: stdout (JSON)
    Service-->>Route: issue object
    Route-->>API: { success: true, issue }
    API-->>UI: response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Poem

🐰 I sniffed the bd path through tunnels and moss,
ExecFile now hops instead of stringed boss.
Routes got a handler, dialogs got neat,
Columns know IDs — the board feels complete.
Hop, drag, drop — celebrate with a carrot treat! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: implementing a full Kanban board UI for the Beads issue tracking system. It directly matches the primary objective of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

@gemini-code-assist
Copy link

Summary of Changes

Hello @0xtsotsi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a significant enhancement to DevFlow by integrating a full-featured Kanban board for the Beads issue tracking system. The primary goal is to provide users with a visual and intuitive interface to manage tasks, bugs, and features, leveraging the dependency-aware capabilities of Beads. This integration spans both frontend and backend, introducing new UI components, custom hooks, API endpoints, and services to ensure seamless interaction with the underlying Beads CLI. The changes also include comprehensive documentation and helper scripts to streamline the development workflow and agent interaction with the new system.

Highlights

  • Full Kanban Board UI: Introduced a complete Kanban board interface for the Beads issue tracking system, providing a visual and interactive way to manage issues within DevFlow. This includes main view components, header with stats, and individual issue cards.
  • New Frontend Components & Hooks: Added several new React components such as beads-view.tsx, BeadsHeader, BeadsKanbanBoard, BeadsCard, BeadsColumn, and BeadsBadges to render the Kanban board. Custom hooks like useBeadsIssues, useBeadsColumnIssues, useBeadsActions, and useBeadsDragDrop were implemented to manage data, state, and drag-and-drop functionality.
  • Issue Management Dialogs: Implemented dedicated dialogs for CreateIssueDialog, EditIssueDialog, and DeleteIssueDialog to facilitate full CRUD operations for Beads issues directly from the UI.
  • Beads API Integration: Integrated the Beads CLI (bd) through new backend routes (/api/beads) and a dedicated BeadsService. This allows the frontend to interact with the Beads issue tracker for listing, creating, updating, deleting, and validating issues, as well as fetching 'ready' work.
  • Documentation & Tooling Updates: Updated README.md with a new 'Beads UI Integration' section, detailing installation, usage, and helper npm scripts. The AGENTS.md file was also significantly expanded with comprehensive guidelines for AI agents using the Beads CLI for task management.
  • Global State Management: The app-store.ts was updated to include Beads-specific state, allowing for centralized management of issues, ready work, filters, and UI settings across projects.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Dec 24, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #6

Docstrings generation was requested by @0xtsotsi.

* #5 (comment)

The following files were modified:

* `apps/server/src/routes/beads/common.ts`
* `apps/server/src/routes/beads/index.ts`
* `apps/server/src/routes/beads/routes/create.ts`
* `apps/server/src/routes/beads/routes/delete.ts`
* `apps/server/src/routes/beads/routes/list.ts`
* `apps/server/src/routes/beads/routes/ready.ts`
* `apps/server/src/routes/beads/routes/update.ts`
* `apps/server/src/routes/beads/routes/validate.ts`
* `apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts`
* `apps/ui/src/components/views/beads-view.tsx`
* `apps/ui/src/components/views/beads-view/components/beads-badges.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts`
@0xtsotsi
Copy link
Owner Author

@claude review and create issues metioning @claude if there are any

Copy link

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

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

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

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

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

Comment on lines 766 to 767
create: (projectPath: string, input: CreateBeadsIssueInput) =>
this.post('/api/beads/create', { projectPath, ...input }),

Choose a reason for hiding this comment

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

P1 Badge Send create payload under issue key

The client posts projectPath plus the issue fields at the top level, but the server’s create handler reads req.body.issue and rejects the request when issue.title is missing. As implemented, UI creates will consistently 400 with “issue.title is required,” so no issues can be created. Either wrap the payload as { projectPath, issue: input } in the client or update the server contract (see apps/server/src/routes/beads/routes/create.ts).

Useful? React with 👍 / 👎.

Comment on lines 768 to 769
update: (projectPath: string, issueId: string, updates: UpdateBeadsIssueInput) =>
this.post('/api/beads/update', { projectPath, issueId, ...updates }),

Choose a reason for hiding this comment

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

P1 Badge Send update payload under updates key

The client flattens the update fields into the request body, but the server’s update handler expects them under req.body.updates. This causes every update to fail with “updates are required,” breaking status changes and edits. Wrap the payload as { projectPath, issueId, updates } or adjust the server route (see apps/server/src/routes/beads/routes/update.ts).

Useful? React with 👍 / 👎.

Comment on lines 176 to 180
let command = `bd create "${input.title}" --json`;

if (input.description) {
command += ` --description "${input.description}"`;
}

Choose a reason for hiding this comment

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

P1 Badge Avoid shell interpolation for issue fields

User-supplied issue data is interpolated directly into a shell command string. If a title/description contains quotes or shell metacharacters (e.g., "; rm -rf /), exec will execute unintended commands in the server context. Use execFile/spawn with an argument array or escape inputs before invoking the CLI to prevent command injection.

Useful? React with 👍 / 👎.

@0xtsotsi
Copy link
Owner Author

@codex adress feedback

@0xtsotsi
Copy link
Owner Author

@claude adress feedback

@chatgpt-codex-connector
Copy link

Summary

  • Updated the Beads API client to wrap create/update payloads in the expected issue and updates keys.
  • Hardened Beads CLI invocation by switching to execFile with argument arrays across list/create/update/get/delete operations.

Testing

  • npm run typecheck --workspace=apps/ui (missing script: "typecheck" in @automaker/ui)
  • npm run lint --workspace=apps/ui (fails with existing lint errors in repo)

Notes

  • bd CLI is not installed in this environment, so Beads sync/issue commands could not be run.

View task →

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant new feature: a Kanban board UI for the Beads issue tracking system. The changes are extensive, covering backend API endpoints, a new service to interact with the bd CLI, and a full set of frontend components, hooks, and state management for the UI.

Overall, this is a great addition. However, I've identified several critical issues that need to be addressed:

  • A command injection vulnerability in the BeadsService on the backend.
  • A few bugs on the frontend that would cause runtime errors or incorrect behavior, such as a ReferenceError due to out-of-order declaration, and incorrect API payload construction.
  • Some logical flaws, like an unreachable code path that would leave the "Backlog" column perpetually empty.

My review includes detailed comments and suggestions to fix these issues. Once these are addressed, this will be a solid feature.

Comment on lines 175 to 194
try {
let command = `bd create "${input.title}" --json`;

if (input.description) {
command += ` --description "${input.description}"`;
}
if (input.type) {
command += ` --type ${input.type}`;
}
if (input.priority !== undefined) {
command += ` --priority ${input.priority}`;
}
if (input.labels?.length) {
command += ` --labels ${input.labels.join(',')}`;
}

const { stdout } = await execAsync(command, { cwd: projectPath });
const issue = JSON.parse(stdout);
return issue;
} catch (error) {

Choose a reason for hiding this comment

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

security-critical critical

The current implementation of building the command string is vulnerable to command injection. User-provided input like input.title and input.description are directly concatenated into the command string. A malicious input could execute arbitrary shell commands.

To fix this, you should use execFile which takes arguments as an array, preventing shell interpretation of the arguments. You'll also need to update the imports at the top of the file:

import { execFile } from 'child_process';
import { promisify } from 'util';

const execFileAsync = promisify(execFile);

This same vulnerability exists in listIssues, updateIssue, deleteIssue, addDependency, and removeDependency and they should all be refactored in a similar way.

    try {
      const args: string[] = ['create', input.title, '--json'];

      if (input.description) {
        args.push('--description', input.description);
      }
      if (input.type) {
        args.push('--type', input.type);
      }
      if (input.priority !== undefined) {
        args.push('--priority', String(input.priority));
      }
      if (input.labels?.length) {
        args.push('--labels', input.labels.join(','));
      }

      const { stdout } = await execFileAsync('bd', args, { cwd: projectPath });
      const issue = JSON.parse(stdout);
      return issue;
    }

const { issues, isLoading, error, loadIssues } = useBeadsIssues({ currentProject });
const { columnIssuesMap, stats } = useBeadsColumnIssues({
issues,
searchQuery,

Choose a reason for hiding this comment

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

critical

searchQuery is used here but it's declared on line 49. This will cause a ReferenceError at runtime. Please move the declaration of searchQuery state (const [searchQuery, setSearchQuery] = useState('');) to before line 27.

Comment on lines 766 to 769
create: (projectPath: string, input: CreateBeadsIssueInput) =>
this.post('/api/beads/create', { projectPath, ...input }),
update: (projectPath: string, issueId: string, updates: UpdateBeadsIssueInput) =>
this.post('/api/beads/update', { projectPath, issueId, ...updates }),

Choose a reason for hiding this comment

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

critical

The request body for beads.create and beads.update is structured incorrectly. The backend expects the input and updates objects to be nested under issue and updates keys respectively, but they are being spread into the top-level of the request body. This will cause the API calls to fail.

Suggested change
create: (projectPath: string, input: CreateBeadsIssueInput) =>
this.post('/api/beads/create', { projectPath, ...input }),
update: (projectPath: string, issueId: string, updates: UpdateBeadsIssueInput) =>
this.post('/api/beads/update', { projectPath, issueId, ...updates }),
create: (projectPath: string, input: CreateBeadsIssueInput) =>
this.post('/api/beads/create', { projectPath, issue: input }),
update: (projectPath: string, issueId: string, updates: UpdateBeadsIssueInput) =>
this.post('/api/beads/update', { projectPath, issueId, updates }),

Comment on lines 2638 to 2653
addBeadsIssue: (projectPath, issue) => {
const beadsByProject = get().beadsByProject;
const current = beadsByProject[projectPath];
if (!current) return;

set({
beadsByProject: {
...beadsByProject,
[projectPath]: {
...current,
issues: [...current.issues, issue],
lastUpdated: Date.now(),
},
},
});
},

Choose a reason for hiding this comment

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

high

The addBeadsIssue action doesn't handle the case where beadsByProject[projectPath] is not yet initialized. If an issue is added before any issues are loaded for that project, the if (!current) return; guard will cause the action to fail silently. The state for the project should be initialized if it doesn't exist. The same issue exists in removeBeadsIssue.

      addBeadsIssue: (projectPath, issue) => {
        const beadsByProject = get().beadsByProject;
        const current = beadsByProject[projectPath] || {
          issues: [],
          readyWork: [],
          lastUpdated: 0,
          isWatching: false,
        };

        set({
          beadsByProject: {
            ...beadsByProject,
            [projectPath]: {
              ...current,
              issues: [...current.issues, issue],
              lastUpdated: Date.now(),
            },
          },
        });
      },

/** Issue title */
title: string;
/** Detailed description */
description: string;

Choose a reason for hiding this comment

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

high

The description property in CreateBeadsIssueInput is marked as required (string), but it should be optional. The backend service and frontend dialogs both treat it as optional. This type mismatch can lead to bugs and compilation errors.

Suggested change
description: string;
description?: string;

Comment on lines 82 to 84
// Other cases go to backlog
map.backlog.push(issue);
}

Choose a reason for hiding this comment

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

medium

The else block that pushes issues to the backlog is unreachable. The issue.status can only be 'open', 'in_progress', or 'closed', all of which are handled by the preceding if/else if statements. This results in the "Backlog" column always being empty. You need to define logic to differentiate which open and unblocked issues should be in 'Backlog' versus 'Ready'.

Comment on lines 78 to 80
} else {
targetColumn = 'backlog';
}

Choose a reason for hiding this comment

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

medium

Similar to use-beads-column-issues.ts, the else block to assign targetColumn = 'backlog' is unreachable. This means it's impossible to drag and drop an issue into the "Backlog" column by dropping it on another issue.


const currentPath = currentProject.path;
const previousPath = prevProjectPathRef.current;
const isProjectSwitch = previousPath !== null && currentPath !== previousPath;

Choose a reason for hiding this comment

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

medium

The variable isProjectSwitch is declared but never used. This should be removed to improve code clarity.

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

🧹 Nitpick comments (19)
.env.example (1)

19-21: Potential PORT variable naming collision.

Both Beads UI and Automaker Server use a PORT environment variable (lines 21 and 48), but with different defaults (3000 vs 3008). While they serve different purposes, using the same variable name could lead to confusion or misconfiguration.

Consider using more specific variable names to avoid ambiguity:

Suggested naming improvement
 # Beads UI server port (default: 3000)
 # Note: This may conflict with other services running on port 3000
-# PORT=3000
+# BDUI_PORT=3000

 # Port to run the Automaker server on (default: 3008)
-PORT=3008
+AUTOMAKER_PORT=3008

If this change is made, ensure the corresponding code that reads these environment variables is updated accordingly (in Beads UI start scripts and Automaker server initialization).

Also applies to: 47-48

apps/server/src/routes/beads/routes/update.ts (1)

35-38: Consider validating that updates contains at least one field.

The current validation checks if updates is falsy but allows an empty object {}. While this won't cause errors, it results in a no-op update. Consider adding validation to ensure at least one update field is provided.

🔎 Proposed enhancement
 if (!updates) {
   res.status(400).json({ success: false, error: 'updates are required' });
   return;
 }
+
+if (Object.keys(updates).length === 0) {
+  res.status(400).json({ success: false, error: 'updates must contain at least one field' });
+  return;
+}
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)

32-39: Centralize ISSUE_TYPES and PRIORITY_OPTIONS to avoid duplication.

These constants are duplicated in edit-issue-dialog.tsx (lines 37-44). Since DEFAULT_PRIORITY is already imported from the constants file, these arrays should be moved there as well to maintain consistency and reduce duplication.

🔎 Recommended approach

Move to apps/ui/src/components/views/beads-view/constants.ts:

export const ISSUE_TYPES: BeadsIssueType[] = ['bug', 'feature', 'task', 'epic', 'chore'];

export const PRIORITY_OPTIONS: { value: BeadsIssuePriority; label: string }[] = [
  { value: 0, label: 'CRITICAL' },
  { value: 1, label: 'HIGH' },
  { value: 2, label: 'MEDIUM' },
  { value: 3, label: 'LOW' },
  { value: 4, label: 'LOWEST' },
];

Then import in both dialog files:

-const ISSUE_TYPES: BeadsIssueType[] = ['bug', 'feature', 'task', 'epic', 'chore'];
-const PRIORITY_OPTIONS: { value: BeadsIssuePriority; label: string }[] = [
-  { value: 0, label: 'CRITICAL' },
-  { value: 1, label: 'HIGH' },
-  { value: 2, label: 'MEDIUM' },
-  { value: 3, label: 'LOW' },
-  { value: 4, label: 'LOWEST' },
-];
+import { ISSUE_TYPES, PRIORITY_OPTIONS, DEFAULT_PRIORITY } from '../constants';
apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)

37-44: Centralize ISSUE_TYPES and PRIORITY_OPTIONS to avoid duplication.

These constants are duplicated from create-issue-dialog.tsx (lines 32-39). Move them to apps/ui/src/components/views/beads-view/constants.ts alongside DEFAULT_PRIORITY for consistency and maintainability.

apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts (1)

12-15: loadIssues prop is declared but never used.

The loadIssues callback is part of the hook's props interface but is not called anywhere in the implementation. Either remove it from the interface or use it to refresh data after mutations (e.g., after successful create/update/delete operations) if full reload is needed.

🔎 Option 1: Remove unused prop
 interface UseBeadsActionsProps {
   currentProject: { path: string } | null;
-  loadIssues: () => Promise<void>;
 }

-export function useBeadsActions({ currentProject, loadIssues }: UseBeadsActionsProps) {
+export function useBeadsActions({ currentProject }: UseBeadsActionsProps) {
apps/ui/src/components/views/beads-view/beads-header.tsx (2)

11-12: Unused imports: Filter icon and cn utility.

Both Filter and cn are imported but not used in this component. The Filter icon is rendered inside the SelectTrigger, but cn has no usage.

🔎 Remove unused import
-import { cn } from '@/lib/utils';

79-95: Type filter is non-functional.

The Select component has a default value but no onValueChange handler, so selecting a type has no effect. Consider either wiring it to the parent's filter state or removing it until the filtering feature is implemented to avoid confusing users.

apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)

24-29: isProjectSwitch is computed but never used.

The variable is calculated on line 26 but has no consumers in the function body.

🔎 Remove unused variable
-    const currentPath = currentProject.path;
-    const previousPath = prevProjectPathRef.current;
-    const isProjectSwitch = previousPath !== null && currentPath !== previousPath;
+    const currentPath = currentProject.path;

     // Update the ref to track current project
     prevProjectPathRef.current = currentPath;
apps/server/src/routes/beads/routes/ready.ts (1)

12-15: Consider validating limit parameter.

The limit parameter is passed directly to the service without validation. If a negative number or non-integer is provided, it could cause unexpected behavior in the CLI command.

🔎 Add basic validation
       const { projectPath, limit } = req.body as {
         projectPath: string;
         limit?: number;
       };

       if (!projectPath) {
         res.status(400).json({ success: false, error: 'projectPath is required' });
         return;
       }

+      if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) {
+        res.status(400).json({ success: false, error: 'limit must be a positive integer' });
+        return;
+      }
+
       const issues = await beadsService.getReadyWork(projectPath, limit);
apps/ui/src/components/views/beads-view/components/beads-card.tsx (2)

60-69: Redundant cursor-grab class on line 61.

The cursor-grab class is applied unconditionally on line 61, but then conditionally applied again on line 66. The first occurrence is redundant and overridden by lines 66-67.

🔎 Proposed fix
       className={cn(
-        'cursor-grab active:cursor-grabbing relative select-none',
+        'active:cursor-grabbing relative select-none',
         'transition-all duration-200 ease-out',
         'shadow-sm hover:shadow-md hover:shadow-black/10',
         'hover:-translate-y-0.5',
         'border-border/50',
         canDrag && 'cursor-grab',
         !canDrag && 'cursor-default',
         isDragging && 'scale-105 shadow-xl shadow-black/20 rotate-1'
       )}

111-113: Status display shows lowercase for 'open' and 'closed'.

The in_progress status is explicitly formatted as "In Progress", but other statuses display as-is (lowercase). Consider capitalizing them for consistency.

🔎 Capitalize all statuses
-            {issue.status === 'in_progress' ? 'In Progress' : issue.status.replace('_', ' ')}
+            {issue.status === 'in_progress'
+              ? 'In Progress'
+              : issue.status.charAt(0).toUpperCase() + issue.status.slice(1).replace('_', ' ')}
apps/ui/src/lib/http-api-client.ts (1)

32-36: Verify Beads HTTP payload shapes and align with existing patterns

The beads methods look consistent overall, but create and update currently spread their inputs into the top-level body:

  • create: { projectPath, ...input }
  • update: { projectPath, issueId, ...updates }

while other APIs (e.g., features.create/update) use nested objects ({ projectPath, feature }, { projectPath, featureId, updates }). If the Beads server routes expect { projectPath, input } / { projectPath, issueId, updates } rather than flattened fields, these calls will break or collide with similarly named fields.

Consider matching the existing pattern for clarity and safety:

create: (projectPath, input) =>
  this.post('/api/beads/create', { projectPath, input }),
update: (projectPath, issueId, updates) =>
  this.post('/api/beads/update', { projectPath, issueId, updates }),

and double‑check the route handlers to ensure the payload contracts match.

Also applies to: 762-775

apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts (1)

11-42: Tighten hook API and remove unused / unreachable pieces

Functionally this hook looks solid (search, blocker detection, stats), but there are a few cleanup opportunities:

  • currentProject is accepted in UseBeadsColumnIssuesProps but never used; you can drop it from the props and call sites unless you plan to key behavior on project soon.
  • getBlockingCount is defined but never used or returned; given similar logic also exists in beads-kanban-board.tsx and beads-view.tsx, it’s easy for these to drift. Either export this helper for reuse or remove it.
  • With the current BeadsIssueStatus values ('open' | 'in_progress' | 'closed'), the else branch that pushes to backlog is effectively unreachable, so backlog will always be empty. If backlog is meant to be a distinct state from “Ready”, you’ll likely need an additional field or explicit status to distinguish them.

None of these are blockers, but cleaning them up will make the column logic easier to evolve.

Also applies to: 67-118

apps/ui/src/components/views/beads-view/beads-kanban-board.tsx (1)

22-61: Avoid over-subscribing to the store and centralize dependency logic

A few small improvements here:

  • useAppStore() with no selector will re-render this board whenever any piece of app state changes. Consider a selector or passing the background settings down from the parent to avoid unnecessary re-renders.
  • getBlockingCounts repeats essentially the same dependency/blocker logic already present in use-beads-column-issues.ts (and again in beads-view.tsx). Extracting a shared helper (e.g., computeBeadsBlockingCounts(issues, issue)) would keep behavior consistent and easier to change.
  • handleDragStart/handleDragEnd props are typed as any, even though they’re always DnD Kit events. You can tighten these to DragStartEvent / DragEndEvent from @dnd-kit/core for better safety and IntelliSense.

All of this is incremental and can be done later without changing behavior.

Also applies to: 67-115

apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)

17-24: Reduce duplication and hard-coded column IDs in drag/drop logic

The status mapping and drop handling look correct, but they’re a bit brittle:

  • columnToStatus and the columnIds array are both hard-coded. If BEADS_COLUMNS changes (e.g., new column IDs), this hook will silently fall out of sync.
  • The blocker detection here mirrors logic in use-beads-column-issues.ts and beads-kanban-board.tsx.

Consider:

  • Deriving columnIds from BEADS_COLUMNS.map(c => c.id) and, if needed, exporting a shared columnToStatus map from your constants module.
  • Extracting blocker / blocked-count helpers into a shared Beads utility to keep column mapping and DnD behavior consistent.

This keeps drag/drop behavior aligned with the rest of the Beads board as it evolves.

Also applies to: 51-88

apps/ui/src/components/views/beads-view/components/beads-badges.tsx (1)

10-41: Badge components are cohesive and handle empty cases well

These small components are nicely factored:

  • Type/priority colors are centralized via constants.
  • Blocking/blocked badges and label chips correctly no-op when counts/labels are empty.

If you ever expect out-of-range priorities, you might add a fallback label/color, but the current behavior is safe.

Also applies to: 48-85, 92-106

apps/ui/src/components/views/beads-view.tsx (1)

51-71: Deduplicate dependency counting and drop unused blockingCount read

You now have essentially the same “blocks / blocked by” logic in three places:

  • use-beads-column-issues.ts (hasOpenBlockers / getBlockingCount)
  • beads-kanban-board.tsx (getBlockingCounts)
  • This file (getBlockingCounts)

Additionally, inside handleConfirmDelete:

const { blockingCount } = getBlockingCounts(selectedIssue);
const success = await handleDeleteIssue(selectedIssue.id, selectedIssue.title);

blockingCount is computed but never used—the dialog already receives blockingCount via:

blockingCount={selectedIssue ? getBlockingCounts(selectedIssue).blockingCount : 0}

To reduce duplication and dead code:

  • Extract a shared helper (e.g., computeBeadsBlockingCounts(issues, issue)) into a small Beads utils module and use it in the hook, board, and view.
  • Remove the unused blockingCount local inside handleConfirmDelete unless you plan to gate deletion on it.

This keeps dependency semantics centralized and avoids subtle drift over time.

Also applies to: 86-99, 217-244

apps/ui/src/lib/electron.ts (1)

4-11: BeadsAPI integration into ElectronAPI looks consistent

The new BeadsAPI interface and the optional beads?: BeadsAPI field on ElectronAPI align with the HTTP client’s beads implementation and the Beads types from @automaker/types. This gives the UI a clean, typed surface for Beads operations without impacting existing APIs.

If you plan to support Beads in the mock API path as well, you could later add a beads stub to getMockElectronAPI, but it’s not required for the current HTTP-only flow.

Also applies to: 103-152, 394-455

apps/server/src/services/beads-service.ts (1)

158-158: Consider sanitizing error messages to avoid information disclosure.

Several methods re-throw errors that include the original error object/message, which might expose sensitive information like file paths, stack traces, or system details. While this aids debugging, consider sanitizing error messages in production or providing a debug mode flag.

Example approach:

// In a shared error handler
function sanitizeError(error: any, context: string): Error {
  if (process.env.NODE_ENV === 'production') {
    return new Error(`${context} failed`);
  }
  return new Error(`${context}: ${error}`);
}

// Usage
catch (error) {
  throw sanitizeError(error, 'Failed to create issue');
}

Also applies to: 195-195, 240-240, 252-252, 269-269, 281-281, 333-333

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 909ebfc and e5d51b3.

⛔ Files ignored due to path filters (2)
  • .beads/beads.db is excluded by !**/*.db
  • .beads/daemon.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • .beads/.local_version
  • .env.example
  • AGENTS.md
  • README.md
  • apps/server/src/index.ts
  • apps/server/src/routes/beads/common.ts
  • apps/server/src/routes/beads/index.ts
  • apps/server/src/routes/beads/routes/create.ts
  • apps/server/src/routes/beads/routes/delete.ts
  • apps/server/src/routes/beads/routes/list.ts
  • apps/server/src/routes/beads/routes/ready.ts
  • apps/server/src/routes/beads/routes/update.ts
  • apps/server/src/routes/beads/routes/validate.ts
  • apps/server/src/services/beads-service.ts
  • apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts
  • apps/ui/src/components/views/beads-view.tsx
  • apps/ui/src/components/views/beads-view/beads-header.tsx
  • apps/ui/src/components/views/beads-view/beads-kanban-board.tsx
  • apps/ui/src/components/views/beads-view/components/beads-badges.tsx
  • apps/ui/src/components/views/beads-view/components/beads-card.tsx
  • apps/ui/src/components/views/beads-view/components/beads-column.tsx
  • apps/ui/src/components/views/beads-view/constants.ts
  • apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/store/app-store.ts
  • libs/types/src/beads.ts
  • libs/types/src/index.ts
  • lint-server-output.txt
  • lint-ui-output.txt
  • package.json
  • typecheck-server-output.txt
  • typecheck-ui-output.txt
🧰 Additional context used
🧬 Code graph analysis (22)
apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/ui/src/components/views/beads-view/components/beads-column.tsx (2)
apps/ui/src/components/views/beads-view/constants.ts (2)
  • BeadsColumnId (45-45)
  • BeadsColumn (5-10)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts (2)
apps/ui/src/components/views/beads-view/constants.ts (1)
  • BeadsColumnId (45-45)
.github/scripts/upload-to-r2.js (1)
  • stats (43-43)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (2)
apps/ui/src/components/views/beads-view/constants.ts (1)
  • DEFAULT_PRIORITY (74-74)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (689-698)
apps/ui/src/lib/electron.ts (1)
libs/types/src/index.ts (4)
  • ListBeadsIssuesFilters (95-95)
  • BeadsIssue (87-87)
  • CreateBeadsIssueInput (93-93)
  • UpdateBeadsIssueInput (94-94)
apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx (1)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/server/src/routes/beads/routes/update.ts (1)
apps/server/src/routes/beads/common.ts (2)
  • logError (12-15)
  • getErrorMessage (5-10)
apps/ui/src/components/views/beads-view/components/beads-card.tsx (2)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
apps/ui/src/components/views/beads-view/components/beads-badges.tsx (5)
  • TypeBadge (10-24)
  • PriorityIndicator (31-41)
  • BlockingBadge (48-63)
  • BlockedBadge (70-85)
  • LabelsList (92-107)
apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)
apps/ui/src/components/views/beads-view/constants.ts (1)
  • BeadsColumnId (45-45)
apps/ui/src/components/views/beads-view/beads-kanban-board.tsx (5)
apps/ui/src/components/views/beads-view/constants.ts (2)
  • BEADS_COLUMNS (12-43)
  • BeadsColumn (5-10)
apps/ui/src/store/app-store.ts (1)
  • useAppStore (988-2894)
apps/ui/src/components/views/beads-view/components/beads-column.tsx (1)
  • BeadsColumn (20-89)
apps/ui/src/components/views/beads-view/components/beads-card.tsx (1)
  • BeadsCard (34-170)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
apps/ui/src/components/views/beads-view/components/beads-badges.tsx (2)
apps/ui/src/components/views/beads-view/constants.ts (3)
  • TYPE_COLORS (66-72)
  • PRIORITY_LABELS (48-54)
  • PRIORITY_COLORS (57-63)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
apps/server/src/routes/beads/index.ts (9)
apps/server/src/services/beads-service.ts (1)
  • BeadsService (15-378)
apps/ui/src/utils/router.ts (1)
  • router (11-15)
apps/server/src/middleware/validate-paths.ts (1)
  • validatePathParams (22-69)
apps/server/src/routes/beads/routes/list.ts (1)
  • createListHandler (9-38)
apps/server/src/routes/beads/routes/create.ts (1)
  • createCreateHandler (9-40)
apps/server/src/routes/beads/routes/update.ts (1)
  • createUpdateHandler (9-47)
apps/server/src/routes/beads/routes/delete.ts (1)
  • createDeleteHandler (9-35)
apps/server/src/routes/beads/routes/ready.ts (1)
  • createReadyWorkHandler (9-29)
apps/server/src/routes/beads/routes/validate.ts (1)
  • createValidateHandler (9-29)
apps/ui/src/components/views/beads-view.tsx (8)
apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)
  • useBeadsIssues (11-86)
apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)
  • useBeadsDragDrop (14-103)
libs/types/src/beads.ts (1)
  • BeadsIssue (36-65)
libs/types/src/index.ts (1)
  • BeadsIssue (87-87)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (689-698)
apps/ui/src/components/views/beads-view/beads-header.tsx (1)
  • BeadsHeader (23-105)
apps/ui/src/components/views/beads-view/beads-kanban-board.tsx (1)
  • BeadsKanbanBoard (22-116)
apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx (1)
  • DeleteIssueDialog (22-76)
apps/server/src/index.ts (2)
apps/server/src/services/beads-service.ts (1)
  • BeadsService (15-378)
apps/server/src/routes/beads/index.ts (1)
  • createBeadsRoutes (15-26)
apps/server/src/routes/beads/routes/ready.ts (1)
apps/server/src/routes/beads/common.ts (2)
  • logError (12-15)
  • getErrorMessage (5-10)
apps/server/src/routes/beads/routes/create.ts (2)
apps/server/src/services/beads-service.ts (1)
  • BeadsService (15-378)
apps/server/src/routes/beads/common.ts (2)
  • logError (12-15)
  • getErrorMessage (5-10)
apps/ui/src/components/views/beads-view/constants.ts (1)
apps/ui/src/components/views/beads-view/components/beads-column.tsx (1)
  • BeadsColumn (20-89)
libs/types/src/beads.ts (1)
libs/types/src/index.ts (11)
  • BeadsIssueStatus (88-88)
  • BeadsIssueType (89-89)
  • BeadsIssuePriority (90-90)
  • BeadsDependencyType (91-91)
  • BeadsIssue (87-87)
  • BeadsDependency (92-92)
  • CreateBeadsIssueInput (93-93)
  • UpdateBeadsIssueInput (94-94)
  • ListBeadsIssuesFilters (95-95)
  • BeadsValidationResult (96-96)
  • BeadsStats (97-97)
apps/server/src/routes/beads/routes/validate.ts (2)
apps/server/src/services/beads-service.ts (1)
  • BeadsService (15-378)
apps/server/src/routes/beads/common.ts (2)
  • logError (12-15)
  • getErrorMessage (5-10)
apps/ui/src/store/app-store.ts (1)
libs/types/src/beads.ts (3)
  • BeadsIssue (36-65)
  • BeadsIssueStatus (12-12)
  • BeadsIssueType (17-17)
apps/ui/src/lib/http-api-client.ts (2)
apps/ui/src/lib/electron.ts (1)
  • BeadsAPI (104-152)
libs/types/src/index.ts (3)
  • ListBeadsIssuesFilters (95-95)
  • CreateBeadsIssueInput (93-93)
  • UpdateBeadsIssueInput (94-94)
🪛 Biome (2.1.2)
apps/ui/src/components/views/beads-view.tsx

[error] 29-29: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

🪛 markdownlint-cli2 (0.18.1)
AGENTS.md

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

(MD040, fenced-code-language)

🔇 Additional comments (28)
.beads/.local_version (1)

1-1: LGTM! Version file looks good.

The version format follows semantic versioning conventions. This local version file will help track Beads feature releases.

README.md (2)

238-383: Comprehensive Beads UI documentation added.

The documentation is well-structured and covers installation, usage patterns, troubleshooting, and integration notes. The inclusion of keyboard shortcuts, npm helper scripts, and troubleshooting scenarios will help users get started quickly.


382-382: Good catch on the port conflict note.

The documentation correctly identifies that the default Beads UI port (3000) may conflict with Automaker's web mode. Users are directed to use the --port flag to specify an alternative.

.env.example (1)

1-61: Well-structured environment configuration template.

The .env.example file is comprehensive and includes helpful comments explaining each variable's purpose, defaults, and usage. The separation between Beads UI and Automaker Server configuration makes it easy to understand.

package.json (1)

48-52: New Beads npm scripts added successfully.

The scripts provide convenient wrappers for common Beads UI operations and align with the documentation in README.md. The script names follow a clear naming convention.

These scripts assume bdui and bd are globally installed. Consider adding graceful error handling or a verification step. Users who haven't installed beads-ui globally will encounter "command not found" errors.

You may want to add a script to check for Beads installation:

"beads:check": "command -v bdui >/dev/null 2>&1 && command -v bd >/dev/null 2>&1 && echo '✓ Beads CLI tools are installed' || echo '✗ Missing beads-ui or bd CLI. Run: npm install -g beads-ui'",

This would help users quickly diagnose installation issues.

AGENTS.md (2)

1-410: Excellent comprehensive agent guidelines document.

This document provides thorough guidance for AI agents working with the Beads task management system. The structure is logical, examples are clear, and the session-ending protocol ensures proper state management. This will be valuable for maintaining consistency across agent sessions.


213-262: Critical session-ending protocol is well-documented.

The step-by-step protocol for ending sessions (file/update work, run quality gates, sync database, verify clean state, provide next session context) is comprehensive and ensures no work is lost. The emphasis on running type checks and linting before committing is good practice.

libs/types/src/index.ts (1)

85-98: Beads type exports added correctly.

The new Beads type exports follow the established pattern in the file, using type-only exports with a clear comment header. The types cover the full range of Beads functionality (issues, statuses, priorities, dependencies, inputs, and stats).

apps/server/src/index.ts (1)

51-52: BeadsService is correctly designed without service-level initialization.

BeadsService is a stateless CLI wrapper that doesn't require service-level initialization. Unlike AgentService, which maintains internal state and must be explicitly initialized, BeadsService encapsulates direct CLI invocations and operates statelessly. Each method independently calls the Beads CLI commands, making the service ready to use immediately after instantiation. The pattern difference is intentional and appropriate.

apps/server/src/routes/beads/routes/delete.ts (1)

9-35: LGTM!

The delete handler follows the established pattern with proper validation, error handling, and support for optional force deletion. Implementation is clean and consistent with other route handlers.

apps/server/src/routes/beads/common.ts (1)

5-15: LGTM!

These utility functions provide a clean, centralized approach to error handling across Beads routes. The implementation is straightforward and appropriate for the use case.

apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)

41-93: LGTM!

The component logic is well-structured with proper form state management, validation, and user interaction handlers. The form reset on dialog open (lines 51-60) and the Enter key handler for labels (lines 174-178) provide good UX.

apps/server/src/routes/beads/routes/create.ts (1)

9-40: LGTM!

The create handler is well-structured with appropriate validation for both required fields (projectPath and issue.title) and consistent error handling. The implementation aligns with the established pattern across other Beads route handlers.

apps/server/src/routes/beads/routes/list.ts (1)

9-38: LGTM!

The list handler properly supports optional filtering while maintaining consistent validation and error handling patterns. The implementation is clean and follows the established route handler conventions.

apps/server/src/routes/beads/index.ts (1)

15-26: LGTM!

The route assembly is clean and well-organized. All endpoints are properly wired with appropriate middleware. The /validate route correctly omits validatePathParams since it accepts an optional projectPath parameter.

apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)

46-98: LGTM!

The component logic is well-implemented with proper form population from the issue prop (lines 56-65) and appropriate validation. The structure mirrors the create dialog, providing a consistent user experience across both workflows.

apps/server/src/routes/beads/routes/validate.ts (1)

9-29: LGTM! Clean implementation following established patterns.

The handler correctly branches between project-specific validation and global installation checks, with consistent error handling using the shared utilities.

apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts (1)

20-57: LGTM! Well-structured action handlers with consistent patterns.

The handlers properly validate preconditions, interact with the Electron API, update local store state on success, and provide clear user feedback via toasts. Error handling is robust with both console logging and user-facing messages.

apps/ui/src/components/views/beads-view/beads-header.tsx (1)

23-105: Overall structure looks good.

The component is well-organized with clear separation between stats display, search, filters, and actions. The memoization is appropriate given the props structure.

apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)

75-85: LGTM! Smart loading state management.

The hook correctly distinguishes between initial load and project switches, avoiding unnecessary loading spinners. The data derivation from the store with proper fallback to empty array is clean.

apps/server/src/routes/beads/routes/ready.ts (1)

9-29: Handler follows established patterns.

The structure is consistent with other Beads route handlers, with proper error handling and response formatting.

apps/ui/src/components/views/beads-view/components/beads-column.tsx (1)

33-88: Good visual feedback for drag-and-drop states.

The component provides clear visual cues during drag operations with ring effects, background color changes, and a dashed border overlay. The opacity control for the background layer is a nice touch for customization.

apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx (1)

22-76: Well-structured confirmation dialog with appropriate warning.

The dialog effectively communicates the destructive action with visual cues (destructive title, AlertTriangle icon) and provides relevant context about blocking dependencies.

apps/ui/src/components/views/beads-view/components/beads-card.tsx (1)

34-170: Solid implementation with proper drag-drop and action handling.

The card correctly integrates with useSortable, disables dragging for closed issues, and provides context-appropriate actions. Event propagation is handled properly throughout the action menu.

apps/ui/src/components/views/beads-view/constants.ts (1)

12-74: Beads board constants look consistent and well-structured

The column definitions, priority labels/colors, and type colors are coherent and match the rest of the Beads UI usage. Good centralization of these values for reuse across components.

libs/types/src/beads.ts (1)

1-166: LGTM! Well-structured type definitions.

The Beads type definitions are comprehensive, well-documented, and consistent. The interfaces cover all necessary aspects of issue tracking including CRUD operations, filtering, validation, and statistics.

apps/ui/src/store/app-store.ts (2)

535-553: LGTM! Beads state structure follows established patterns.

The Beads state management follows the same per-project pattern used elsewhere in the store (e.g., autoModeByProject, boardBackgroundByProject). The structure includes issues, ready work, last update timestamp, and watching status, which aligns well with the intended functionality.


2596-2739: LGTM! Beads action implementations are consistent and well-structured.

All Beads actions follow the established per-project state management pattern used throughout the store. The implementations correctly:

  • Initialize default state when needed
  • Update lastUpdated timestamp for data changes
  • Preserve existing state when making partial updates
  • Handle edge cases (e.g., returning early if project state doesn't exist)

Comment on lines 311 to 354
```
User: Fix the login form validation bug

Agent: I'll help fix the login form validation. Let me start by checking existing issues.

$ bd ready
# Output: No ready work

$ bd list --title-contains "login"
# Output: No existing login issues

$ bd create "Fix login form validation" --type bug --priority 1 --description "Login form is not validating email format correctly"
# Output: Created bd-a1b2

$ bd update bd-a1b2 --status in_progress

[Agent investigates the code...]

Agent: I found the validation bug. However, I also discovered that the password field has the same issue.

$ bd create "Fix password field validation" --type bug --priority 1 --description "Password field not validating requirements"
# Output: Created bd-c3d4

$ bd dep add bd-c3d4 discovered-from bd-a1b2
$ bd dep add bd-a1b2 related bd-c3d4

[Agent fixes login validation...]

$ bd update bd-a1b2 --status closed
$ bd update bd-c3d4 --status in_progress

[Agent fixes password validation...]

$ bd update bd-c3d4 --status closed

$ bd ready
# Output: No ready work

$ bd sync
$ git add .beads/beads.jsonl
$ git commit -m "Fix login and password validation bugs"

Agent: I've completed both fixes. The login form now validates email format correctly, and the password field enforces the required complexity rules. Both issues are closed and synced.
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing language identifier for fenced code block.

The example agent session code block (line 311) is missing a language identifier, which triggers a markdownlint warning.

Proposed fix
 ### Example Agent Session
 
-```
+```bash
 User: Fix the login form validation bug
 
 Agent: I'll help fix the login form validation. Let me start by checking existing issues.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

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

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In AGENTS.md around lines 311 to 354 the fenced code block lacks a language
identifier causing a markdownlint warning; add an appropriate language tag
(e.g., ```bash or ```text) immediately after the opening triple backticks to
mark the block language, leave the rest of the block intact, and commit the
change.

Comment on lines 21 to 28
async isBeadsInstalled(): Promise<boolean> {
try {
const { stdout } = await execAsync('which bd');
return stdout.trim().length > 0;
} catch {
return false;
}
}
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

Cross-platform compatibility issue with which command.

The which command is Unix-specific and won't work on Windows. This will cause isBeadsInstalled() to always return false on Windows, even if the bd CLI is properly installed and available in PATH.

🔎 Recommended fix using platform-agnostic approach
 async isBeadsInstalled(): Promise<boolean> {
   try {
-    const { stdout } = await execAsync('which bd');
-    return stdout.trim().length > 0;
+    // Try running 'bd --version' directly - if it succeeds, bd is installed
+    await execAsync('bd --version');
+    return true;
   } catch {
     return false;
   }
 }

This approach works on all platforms because it directly attempts to execute the bd command rather than relying on Unix-specific utilities.

📝 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
async isBeadsInstalled(): Promise<boolean> {
try {
const { stdout } = await execAsync('which bd');
return stdout.trim().length > 0;
} catch {
return false;
}
}
async isBeadsInstalled(): Promise<boolean> {
try {
// Try running 'bd --version' directly - if it succeeds, bd is installed
await execAsync('bd --version');
return true;
} catch {
return false;
}
}
🤖 Prompt for AI Agents
In apps/server/src/services/beads-service.ts around lines 21–28, the method
currently uses the Unix-only `which` command which fails on Windows; change it
to a cross-platform check by attempting to execute the `bd` binary directly
(e.g. run `bd --version` or similar harmless flag) via execAsync, return true
when the command exits successfully and produces output, and return false on
error; ensure you catch errors from execAsync and treat non-zero/exception as
"not installed" rather than relying on platform-specific utilities.

Comment on lines 135 to 140
{
id: 'beads',
label: 'Beads',
icon: ListTodo,
shortcut: shortcuts.beads,
},
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:

#!/bin/bash
# Search for shortcuts.beads definition
echo "=== Searching for shortcuts.beads definition ==="
rg -n "beads" apps/ui/src --type ts -C 3 | head -100

echo ""
echo "=== Searching for route with 'beads' path ==="
rg -n "path|route" apps/ui/src --type ts | grep -i beads

echo ""
echo "=== Searching for shortcuts configuration ==="
find apps/ui/src -name "*shortcut*" -type f

echo ""
echo "=== Searching for route configuration files ==="
find apps/ui/src -name "*route*" -type f

Repository: 0xtsotsi/DevFlow

Length of output: 8440


🏁 Script executed:

#!/bin/bash
# Check keyboard shortcuts configuration
echo "=== Keyboard Shortcuts Configuration ==="
cat apps/ui/src/hooks/use-keyboard-shortcuts.ts | head -100

echo ""
echo "=== Router Configuration ==="
cat apps/ui/src/utils/router.ts | head -150

Repository: 0xtsotsi/DevFlow

Length of output: 3702


🏁 Script executed:

#!/bin/bash
# Search for shortcuts object definition
echo "=== Searching for shortcuts object definition ==="
rg -n "shortcuts\s*[=:]" apps/ui/src --type ts -A 20 | head -150

echo ""
echo "=== Searching for routeTree file ==="
find apps/ui/src -name "*routeTree*" -type f

echo ""
echo "=== Searching for route definitions with beads ==="
find apps/ui/src -path "*route*" -name "*.tsx" -o -name "*.ts" | head -20

Repository: 0xtsotsi/DevFlow

Length of output: 14397


🏁 Script executed:

#!/bin/bash
# Find useKeyboardShortcutsConfig implementation and DEFAULT_KEYBOARD_SHORTCUTS
echo "=== Searching for useKeyboardShortcutsConfig and DEFAULT_KEYBOARD_SHORTCUTS ==="
rg -n "useKeyboardShortcutsConfig|DEFAULT_KEYBOARD_SHORTCUTS" apps/ui/src --type ts -A 5 | head -200

echo ""
echo "=== Searching for beads in route files ==="
rg -n "beads" apps/ui/src --type tsx | grep -E "(route|to=|href)" | head -30

echo ""
echo "=== Looking for route tree generation or layout routes ==="
find apps/ui/src -name "*.route.tsx" -o -name "layout.tsx" -o -name "*routes*" | grep -v node_modules

Repository: 0xtsotsi/DevFlow

Length of output: 15765


🏁 Script executed:

#!/bin/bash
# Get the full DEFAULT_KEYBOARD_SHORTCUTS definition
echo "=== DEFAULT_KEYBOARD_SHORTCUTS definition ==="
sed -n '180,220p' apps/ui/src/store/app-store.ts

echo ""
echo "=== Routes directory structure ==="
find apps/ui/src/routes -type f -name "*.tsx" | head -30

echo ""
echo "=== Look for beads in routes ==="
grep -r "beads" apps/ui/src/routes --include="*.tsx" -l

Repository: 0xtsotsi/DevFlow

Length of output: 2063


Missing keyboard shortcut and route configuration for Beads navigation.

The navigation item references shortcuts.beads which is not defined in DEFAULT_KEYBOARD_SHORTCUTS in apps/ui/src/store/app-store.ts. Additionally, there is no corresponding route file (beads.tsx) in apps/ui/src/routes/, so the navigation will fail at runtime.

Add the beads shortcut to DEFAULT_KEYBOARD_SHORTCUTS and create the missing beads.tsx route file before this change is complete.

Comment on lines 163 to 191
// Show loading state
if (isLoading) {
return (
<div className="flex items-center justify-center h-full">
<p className="text-muted-foreground">Loading issues...</p>
</div>
);
}

// Show error state
if (error) {
return (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<p className="text-red-500 mb-2">Error loading issues</p>
<p className="text-sm text-muted-foreground">{error}</p>
</div>
</div>
);
}

// Show no project selected state
if (!currentProject) {
return (
<div className="flex items-center justify-center h-full">
<p className="text-muted-foreground">Select a project to use Beads</p>
</div>
);
}
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

Ensure “Select a project” state is reachable (loading branch currently wins)

With useBeadsIssues initializing isLoading to true and short‑circuiting when currentProject is null, isLoading never flips to false for the “no project” case. Given the current order:

  1. if (isLoading) …
  2. if (error) …
  3. if (!currentProject) …

the view will show “Loading issues...” indefinitely instead of “Select a project to use Beads” when no project is selected.

Simplest fix is to check !currentProject first:

-  // Show loading state
-  if (isLoading) {
-    return (
-      <div className="flex items-center justify-center h-full">
-        <p className="text-muted-foreground">Loading issues...</p>
-      </div>
-    );
-  }
-
-  // Show error state
-  if (error) {
-    ...
-  }
-
-  // Show no project selected state
-  if (!currentProject) {
+  // Show no project selected state
+  if (!currentProject) {
     return (
       <div className="flex items-center justify-center h-full">
         <p className="text-muted-foreground">Select a project to use Beads</p>
       </div>
     );
   }
+
+  // Show loading state
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-full">
+        <p className="text-muted-foreground">Loading issues...</p>
+      </div>
+    );
+  }
+
+  // Show error state
+  if (error) {
+    ...
+  }

That way the empty-project case renders correctly without changing the loading behavior once a project is selected.

🤖 Prompt for AI Agents
In apps/ui/src/components/views/beads-view.tsx around lines 163 to 191, the "no
project selected" branch is unreachable because isLoading is checked first (and
starts true), so the component renders "Loading issues..." forever; move the `if
(!currentProject)` check to run before the `isLoading` and `error` checks so the
"Select a project to use Beads" state returns immediately when there is no
currentProject.

Comment on lines 69 to 71
<Button variant="destructive" onClick={onDelete} disabled={isDeleting}>
{isDeleting ? 'Deleting...' : 'Delete Issue'}
</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 | 🟡 Minor

Dialog doesn't close after successful deletion.

The onDelete callback returns a Promise<boolean> indicating success, but the result is not used. The dialog should close automatically when deletion succeeds.

🔎 Proposed fix
-          <Button variant="destructive" onClick={onDelete} disabled={isDeleting}>
+          <Button
+            variant="destructive"
+            onClick={async () => {
+              const success = await onDelete();
+              if (success) {
+                onOpenChange(false);
+              }
+            }}
+            disabled={isDeleting}
+          >
             {isDeleting ? 'Deleting...' : 'Delete Issue'}
           </Button>
📝 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
<Button variant="destructive" onClick={onDelete} disabled={isDeleting}>
{isDeleting ? 'Deleting...' : 'Delete Issue'}
</Button>
<Button
variant="destructive"
onClick={async () => {
const success = await onDelete();
if (success) {
onOpenChange(false);
}
}}
disabled={isDeleting}
>
{isDeleting ? 'Deleting...' : 'Delete Issue'}
</Button>
🤖 Prompt for AI Agents
In apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
around lines 69 to 71, the Delete button invokes onDelete but ignores its
Promise<boolean> result so the dialog never closes after a successful deletion;
update the click handler to await onDelete(), and if it resolves to true call
the dialog close handler (e.g. props.onClose() or setOpen(false)) so the modal
is closed only on success; keep existing isDeleting disable/label behavior and
handle errors/false by not closing.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5d51b3 and 33d8aa6.

📒 Files selected for processing (2)
  • README.md
  • package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md
🔇 Additional comments (1)
package.json (1)

47-47: LGTM!

The comma addition is syntactically correct and necessary to support the additional script entries that follow.

Comment on lines 48 to 52
"beads": "bdui start",
"beads:open": "bdui start --open",
"beads:stop": "bdui stop",
"beads:status": "bdui status",
"beads:issues": "bd issues"
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:

#!/bin/bash
# Check if bdui and bd packages exist on npm

echo "=== Checking npm registry for CLI packages ==="
echo ""
echo "Checking for 'bdui' package on npm..."
curl -s https://registry.npmjs.org/bdui | jq -r '.name // "Package not found"' 2>/dev/null || echo "Package not found or error"

echo ""
echo "Checking for 'bd' package on npm..."
curl -s https://registry.npmjs.org/bd | jq -r '.name // "Package not found"' 2>/dev/null || echo "Package not found or error"

echo ""
echo "Checking for 'beads-ui' package on npm..."
curl -s https://registry.npmjs.org/beads-ui | jq -r '.name // "Package not found"' 2>/dev/null || echo "Package not found or error"

echo ""
echo "Searching for beads-related packages on npm..."
curl -s "https://registry.npmjs.org/-/v1/search?text=beads" 2>/dev/null | jq -r '.objects[0:5][] | "\(.package.name) - \(.package.description // "No description")"' || echo "Search failed or no results"

Repository: 0xtsotsi/DevFlow

Length of output: 652


🏁 Script executed:

# Read the full package.json to verify dependencies sections
cat -n package.json

Repository: 0xtsotsi/DevFlow

Length of output: 4728


Add missing npm dependencies for Beads CLI commands.

The scripts reference bdui and bd commands (lines 48-52), but these packages are not listed in dependencies or devDependencies. These packages exist on npm (beads-ui and @beads/bd) but must be added to this package.json for the scripts to work. Without them, users will encounter "command not found" errors when running any beads:* script.

🤖 Prompt for AI Agents
In package.json around lines 48-52, the scripts use the external CLI tools
`bdui` and `bd` but those packages are not declared; add the corresponding npm
packages ("beads-ui" for `bdui` and "@beads/bd" for `bd`) to either
devDependencies (preferred for CLIs used in scripts) or dependencies, then run
the package manager to install them (e.g., npm install --save-dev beads-ui
@beads/bd or yarn add --dev beads-ui @beads/bd) so the `beads:*` scripts can
find and execute the commands.

@0xtsotsi
Copy link
Owner Author

@claude fix all remaining issues and adress feddbacks lets land this plane and prepair for merge change_logo

…i-payload-shape-and-secure-cli-i

Complete execFile migration in BeadsService helpers
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: 7

♻️ Duplicate comments (5)
apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts (1)

140-145: Beads navigation item integration looks correct.

The navigation item is properly structured with id, label, icon, and shortcut binding. However, as noted in a previous review, ensure that shortcuts.beads is defined in DEFAULT_KEYBOARD_SHORTCUTS in the app store and that a corresponding /beads route file exists, otherwise navigation will fail at runtime.

apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)

34-36: Remove unused isProjectSwitch variable.

The variable is computed but never used. This was flagged in a previous review and should be removed for code clarity.

🔎 Proposed fix
     const currentPath = currentProject.path;
-    const previousPath = prevProjectPathRef.current;
-    const isProjectSwitch = previousPath !== null && currentPath !== previousPath;

     // Update the ref to track current project
     prevProjectPathRef.current = currentPath;
apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)

80-90: Unreachable backlog fallback branch.

As noted in a previous review, the else branch assigning targetColumn = 'backlog' (line 89) is unreachable because all possible BeadsIssueStatus values (closed, in_progress, open) are handled in the preceding conditions. This means dragging onto an issue will never result in the backlog column being selected.

Note: Dropping directly onto the backlog column still works (handled at line 65-66), so this only affects the edge case of dropping onto another issue in the backlog.

🔎 Proposed fix - remove unreachable code or add explicit handling
          if (overIssue.status === 'closed') {
            targetColumn = 'done';
          } else if (overIssue.status === 'in_progress') {
            targetColumn = 'in_progress';
          } else if (blockers) {
            targetColumn = 'blocked';
-          } else if (overIssue.status === 'open') {
-            targetColumn = 'ready';
           } else {
-            targetColumn = 'backlog';
+            // status === 'open' without blockers
+            targetColumn = 'ready';
           }
apps/ui/src/components/views/beads-view.tsx (2)

36-58: Critical: searchQuery used before declaration causes runtime error.

searchQuery is referenced on line 38 in the useBeadsColumnIssues call, but it's not declared until line 58. This causes a ReferenceError due to the temporal dead zone.

🔎 Proposed fix - move searchQuery declaration above hook calls
  const { issues, isLoading, error, loadIssues } = useBeadsIssues({ currentProject });
+
+  // Search state - must be declared before useBeadsColumnIssues
+  const [searchQuery, setSearchQuery] = useState('');
+
  const { columnIssuesMap, stats } = useBeadsColumnIssues({
    issues,
    searchQuery,
    currentProject,
  });
  // ... other hooks ...

  // Dialog states
  const [showCreateDialog, setShowCreateDialog] = useState(false);
  const [showEditDialog, setShowEditDialog] = useState(false);
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);
  const [selectedIssue, setSelectedIssue] = useState<BeadsIssue | null>(null);
-
-  // Search state
-  const [searchQuery, setSearchQuery] = useState('');

172-200: Reorder state checks: !currentProject should come before isLoading.

Since useBeadsIssues initializes isLoading to true and short-circuits when currentProject is null, the loading state never transitions to false for the no-project case. The current order causes "Loading issues..." to display indefinitely instead of "Select a project to use Beads".

🔎 Proposed fix
+  // Show no project selected state first
+  if (!currentProject) {
+    return (
+      <div className="flex items-center justify-center h-full">
+        <p className="text-muted-foreground">Select a project to use Beads</p>
+      </div>
+    );
+  }
+
   // Show loading state
   if (isLoading) {
     return (
       <div className="flex items-center justify-center h-full">
         <p className="text-muted-foreground">Loading issues...</p>
       </div>
     );
   }

   // Show error state
   if (error) {
     return (
       <div className="flex items-center justify-center h-full">
         <div className="text-center">
           <p className="text-red-500 mb-2">Error loading issues</p>
           <p className="text-sm text-muted-foreground">{error}</p>
         </div>
       </div>
     );
   }
-
-  // Show no project selected state
-  if (!currentProject) {
-    return (
-      <div className="flex items-center justify-center h-full">
-        <p className="text-muted-foreground">Select a project to use Beads</p>
-      </div>
-    );
-  }
🧹 Nitpick comments (7)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)

198-205: Consider adding accessible label to the remove button.

The label remove button lacks an accessible name, which may affect screen reader users.

🔎 Proposed fix
                     <button
                       type="button"
                       onClick={() => handleRemoveLabel(label)}
                       className="rounded-full hover:bg-muted-foreground/20"
+                      aria-label={`Remove label ${label}`}
                     >
                       <X className="h-3 w-3" />
                     </button>
apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (2)

37-44: Consider extracting shared constants and label logic.

ISSUE_TYPES, PRIORITY_OPTIONS, handleAddLabel, and handleRemoveLabel are duplicated between CreateIssueDialog and EditIssueDialog. Consider extracting these to a shared module to reduce duplication.

Also applies to: 79-89


207-214: Add accessible label to the remove button.

Same accessibility concern as in CreateIssueDialog - the remove button lacks an accessible name.

🔎 Proposed fix
                     <button
                       type="button"
                       onClick={() => handleRemoveLabel(label)}
                       className="rounded-full hover:bg-muted-foreground/20"
+                      aria-label={`Remove label ${label}`}
                     >
                       <X className="h-3 w-3" />
                     </button>
apps/ui/src/components/views/beads-view.tsx (2)

95-108: Unused blockingCount in handleConfirmDelete.

The blockingCount is computed on line 98 but never used. It appears this was intended for some logic (perhaps a confirmation message) that wasn't implemented. Either remove it or use it.

🔎 Proposed fix - remove unused variable
   const handleConfirmDelete = useCallback(async () => {
     if (!selectedIssue || !currentProject) return false;

-    const { blockingCount } = getBlockingCounts(selectedIssue);
-
     const success = await handleDeleteIssue(selectedIssue.id, selectedIssue.title);

     if (success) {
       setShowDeleteDialog(false);
       setSelectedIssue(null);
     }

     return success;
-  }, [selectedIssue, currentProject, handleDeleteIssue, getBlockingCounts]);
+  }, [selectedIssue, currentProject, handleDeleteIssue]);

164-170: Avoid any type for dialog input.

Using any for the input parameter loses type safety. Use the proper type from the imports.

🔎 Proposed fix
+import type { CreateBeadsIssueInput } from '@automaker/types';
+
   // Handle create issue from dialog
   const handleCreateFromDialog = useCallback(
-    async (input: any) => {
+    async (input: CreateBeadsIssueInput) => {
       const result = await handleCreateIssue(input);
       return result !== null;
     },
     [handleCreateIssue]
   );
apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts (1)

12-15: Unused loadIssues parameter.

The loadIssues function is accepted in the props but never used within the hook. If it's intended for manual refresh after operations, it's currently not being called. Either remove it from the interface or use it where appropriate (e.g., after successful create/update/delete operations as a fallback).

🔎 Option A - Remove if not needed
 interface UseBeadsActionsProps {
   currentProject: { path: string } | null;
-  loadIssues: () => Promise<void>;
 }

-export function useBeadsActions({ currentProject, loadIssues }: UseBeadsActionsProps) {
+export function useBeadsActions({ currentProject }: UseBeadsActionsProps) {

If the caller needs loadIssues to be passed through, consider documenting why it's accepted but not used internally.

Also applies to: 28-29

apps/server/src/routes/beads/index.ts (1)

29-29: Document why /validate omits path validation middleware.

The /validate endpoint is the only route without validatePathParams middleware because projectPath is optional (used for checking installation status when omitted).

🔎 Optional inline comment
+// /validate allows optional projectPath - omit path validation middleware
 router.post('/validate', createValidateHandler(beadsService));
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33d8aa6 and c89e1ea.

📒 Files selected for processing (18)
  • apps/server/src/routes/beads/common.ts
  • apps/server/src/routes/beads/index.ts
  • apps/server/src/routes/beads/routes/create.ts
  • apps/server/src/routes/beads/routes/delete.ts
  • apps/server/src/routes/beads/routes/list.ts
  • apps/server/src/routes/beads/routes/ready.ts
  • apps/server/src/routes/beads/routes/update.ts
  • apps/server/src/routes/beads/routes/validate.ts
  • apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts
  • apps/ui/src/components/views/beads-view.tsx
  • apps/ui/src/components/views/beads-view/components/beads-badges.tsx
  • apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/server/src/routes/beads/routes/validate.ts
  • apps/server/src/routes/beads/routes/delete.ts
  • apps/server/src/routes/beads/routes/create.ts
  • apps/ui/src/components/views/beads-view/components/beads-badges.tsx
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
🧰 Additional context used
🧬 Code graph analysis (6)
apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/server/src/routes/beads/routes/update.ts (1)
apps/server/src/routes/beads/common.ts (2)
  • logError (23-26)
  • getErrorMessage (8-13)
apps/ui/src/components/views/beads-view.tsx (10)
apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)
  • useBeadsIssues (21-96)
apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts (1)
  • useBeadsColumnIssues (19-133)
apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)
  • useBeadsDragDrop (24-113)
libs/types/src/beads.ts (1)
  • BeadsIssue (36-65)
libs/types/src/index.ts (1)
  • BeadsIssue (87-87)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (689-698)
apps/ui/src/components/views/beads-view/beads-kanban-board.tsx (1)
  • BeadsKanbanBoard (22-116)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)
  • CreateIssueDialog (49-223)
apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)
  • EditIssueDialog (58-232)
apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx (1)
  • DeleteIssueDialog (36-90)
apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (1)
apps/ui/src/components/views/beads-view/constants.ts (1)
  • BeadsColumnId (45-45)
apps/server/src/routes/beads/index.ts (8)
apps/server/src/services/beads-service.ts (1)
  • BeadsService (15-378)
apps/server/src/middleware/validate-paths.ts (1)
  • validatePathParams (22-69)
apps/server/src/routes/beads/routes/list.ts (1)
  • createListHandler (20-49)
apps/server/src/routes/beads/routes/create.ts (1)
  • createCreateHandler (19-50)
apps/server/src/routes/beads/routes/update.ts (1)
  • createUpdateHandler (15-53)
apps/server/src/routes/beads/routes/delete.ts (1)
  • createDeleteHandler (15-41)
apps/server/src/routes/beads/routes/ready.ts (1)
  • createReadyWorkHandler (20-40)
apps/server/src/routes/beads/routes/validate.ts (1)
  • createValidateHandler (15-35)
apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts (1)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (689-698)
🪛 Biome (2.1.2)
apps/ui/src/components/views/beads-view.tsx

[error] 38-38: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

🔇 Additional comments (9)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)

82-101: LGTM! Solid form submission handling.

The create flow correctly validates the title, manages loading state, and closes the dialog on success. The onCreate callback is expected to handle errors internally and return false on failure, which keeps the dialog open for retry.

apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts (1)

56-64: Good defensive handling for uninitialized Beads.

The validation check properly handles the case where Beads hasn't been initialized for a project, setting an empty issues array and exiting early without showing an error to the user.

apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx (1)

67-77: LGTM! Proper form synchronization with issue prop.

The useEffect correctly populates all form fields when the issue changes, ensuring the form reflects the current issue state when the dialog opens.

apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts (2)

31-68: LGTM! Clean create handler with proper state synchronization.

The handler correctly validates the project, checks API availability, calls the API, updates the local store optimistically, and provides user feedback via toast. Error handling covers both API errors and exceptions.


146-151: Clean delegation pattern for status changes.

handleStatusChange cleanly delegates to handleUpdateIssue, avoiding code duplication while providing a more specific API for status-only updates.

apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts (2)

27-34: Column-to-status mapping is well-defined.

The mapping correctly handles the semantic difference between UI columns and the underlying issue status model. Multiple columns (backlog, ready, blocked) map to 'open' status, which aligns with the board's visual representation.


47-106: Solid drag-end handling with proper null checks.

The handler correctly:

  • Clears the active issue immediately
  • Validates the drop target exists
  • Determines target column from either direct column drop or via the over-issue's current column
  • Skips the API call if status isn't actually changing
apps/server/src/routes/beads/common.ts (1)

8-26: LGTM!

The error handling utilities are well-designed and provide consistent error messaging across all Beads route handlers.

apps/server/src/routes/beads/index.ts (1)

21-32: LGTM!

The router composition is clean and follows Express best practices. The handler factory pattern enables proper dependency injection, and middleware is applied consistently.

Comment on lines 23 to 35
const { projectPath, filters } = req.body as {
projectPath: string;
filters?: {
status?: string[];
type?: string[];
labels?: string[];
priorityMin?: number;
priorityMax?: number;
titleContains?: string;
descContains?: string;
ids?: string[];
};
};
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

Type assertions bypass runtime validation.

The complex filters object with nested optional properties receives no runtime type validation. Invalid types (e.g., priorityMin: "high" instead of a number) will be passed to the service layer.

This is the same pattern issue identified in update.ts. Consider using a schema validation library like Zod for runtime type safety across all Beads route handlers.

Comment on lines 44 to 47
} catch (error) {
logError(error, 'List issues failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
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

Service error messages exposed to client.

Returning getErrorMessage(error) in 500 responses can leak implementation details. This is the same security posture gap identified in update.ts.

🔎 Proposed fix
 } catch (error) {
   logError(error, 'List issues failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to list issues' });
 }
📝 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
} catch (error) {
logError(error, 'List issues failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
} catch (error) {
logError(error, 'List issues failed');
res.status(500).json({ success: false, error: 'Failed to list issues' });
}
🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/list.ts around lines 44-47, the catch
block returns getErrorMessage(error) to the client which can leak implementation
details; change the response to return a generic error message (e.g. { success:
false, error: 'Internal server error' }) while still calling logError(error,
'List issues failed') so the full error is recorded server-side; optionally
generate and return a short error id/correlation id to the client for support
lookup but do not include the raw error text.

Comment on lines 23 to 26
const { projectPath, limit } = req.body as {
projectPath: string;
limit?: number;
};
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

Type assertions bypass runtime validation.

The optional limit parameter has no runtime type validation. A client could send limit: "five" and it would be passed to the service layer as a string instead of a number.

This is the same pattern issue identified in update.ts and list.ts. Consider implementing schema validation across all Beads route handlers.

🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/ready.ts around lines 23 to 26, the
handler uses a TypeScript type assertion for req.body which bypasses runtime
validation so a client can send limit as a string; replace the assertion with
runtime validation: define/consume a request schema (e.g.
Zod/Joi/express-validator) that enforces projectPath is a string and limit is an
optional number, parse/coerce limit to a number (or reject non-numeric values),
return a 400 error on validation failure, and pass the validated/typed values to
the service; apply the same schema-based validation approach to update.ts and
list.ts.

Comment on lines 35 to 38
} catch (error) {
logError(error, 'Get ready work failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
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

Service error messages exposed to client.

Returning getErrorMessage(error) in 500 responses can leak implementation details. This is the same security posture gap identified in other Beads route handlers.

🔎 Proposed fix
 } catch (error) {
   logError(error, 'Get ready work failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to get ready work' });
 }
🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/ready.ts around lines 35 to 38, the
handler currently returns getErrorMessage(error) in the 500 response which may
leak implementation details; change the response to return a generic error
message (e.g. { success: false, error: 'Internal server error' }) while
retaining the existing logError(error, 'Get ready work failed') so full error
details are recorded in server logs for debugging; do not include stack/traces
or error messages in the JSON sent to the client.

Comment on lines 18 to 29
const { projectPath, issueId, updates } = req.body as {
projectPath: string;
issueId: string;
updates: {
title?: string;
description?: string;
status?: string;
type?: string;
priority?: number;
labels?: string[];
};
};
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

Type assertions bypass runtime validation.

The as type assertion for req.body provides no runtime type safety. If a client sends malformed data (wrong types, unexpected fields), the service layer will receive invalid input.

🔎 Consider using a validation library

Consider using a schema validation library like Zod to validate request bodies at runtime:

import { z } from 'zod';

const updateSchema = z.object({
  projectPath: z.string(),
  issueId: z.string(),
  updates: z.object({
    title: z.string().optional(),
    description: z.string().optional(),
    status: z.string().optional(),
    type: z.string().optional(),
    priority: z.number().optional(),
    labels: z.array(z.string()).optional(),
  }),
});

// In handler:
const parsed = updateSchema.safeParse(req.body);
if (!parsed.success) {
  res.status(400).json({ success: false, error: 'Invalid request body' });
  return;
}
const { projectPath, issueId, updates } = parsed.data;
🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/update.ts around lines 18 to 29, the
handler currently uses a TypeScript type assertion for req.body which provides
no runtime validation; replace it with a runtime schema check (e.g., Zod):
define a schema for projectPath, issueId and updates (with optional title,
description, status, type, priority number, labels array of strings), call
safeParse on req.body, return a 400 response when validation fails, and then
extract projectPath, issueId and updates from the parsed data to pass to the
service layer.

Comment on lines 41 to 44
if (!updates) {
res.status(400).json({ success: false, error: 'updates are required' });
return;
}
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

Empty updates object not rejected.

The validation only checks that updates is truthy, but an empty object {} will pass and make a pointless service call.

🔎 Proposed fix
 if (!updates) {
   res.status(400).json({ success: false, error: 'updates are required' });
   return;
 }
+
+if (Object.keys(updates).length === 0) {
+  res.status(400).json({ success: false, error: 'updates cannot be empty' });
+  return;
+}
📝 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
if (!updates) {
res.status(400).json({ success: false, error: 'updates are required' });
return;
}
if (!updates) {
res.status(400).json({ success: false, error: 'updates are required' });
return;
}
if (Object.keys(updates).length === 0) {
res.status(400).json({ success: false, error: 'updates cannot be empty' });
return;
}
🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/update.ts around lines 41 to 44, the
handler only checks that `updates` is truthy so an empty object {} passes;
change the validation to reject empty objects as well by checking that `updates`
is an object with at least one own property (e.g. ensure typeof updates ===
'object' && updates !== null && Object.keys(updates).length > 0), and if the
check fails return res.status(400).json({ success: false, error: 'updates are
required' }) to avoid making a pointless service call.

Comment on lines 48 to 51
} catch (error) {
logError(error, 'Update issue failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
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

Service error messages exposed to client.

Returning getErrorMessage(error) in 500 responses can leak implementation details (file paths, stack traces, internal service names) if the underlying service throws detailed errors.

🔎 Proposed fix

Return a generic error message to the client and log the detailed error server-side:

 } catch (error) {
   logError(error, 'Update issue failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to update issue' });
 }
📝 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
} catch (error) {
logError(error, 'Update issue failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
} catch (error) {
logError(error, 'Update issue failed');
res.status(500).json({ success: false, error: 'Failed to update issue' });
}
🤖 Prompt for AI Agents
In apps/server/src/routes/beads/routes/update.ts around lines 48 to 51, the
handler currently returns getErrorMessage(error) to the client which can leak
internal details; change the response to return a generic error message (e.g.,
"Internal server error") while keeping the full error logged server-side.
Specifically, keep or enhance logError(error, ...) so it records the detailed
error and stack, then replace res.status(500).json({ success: false, error:
getErrorMessage(error) }) with res.status(500).json({ success: false, error:
'Internal server error' }) (or similar constant) and do not expose the original
error in the response.

…plement-full-kanban-board-ui

Fix Beads API payload shape and secure CLI invocation
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: 0

♻️ Duplicate comments (2)
apps/server/src/services/beads-service.ts (2)

21-28: Cross-platform compatibility issue remains unresolved.

This method still uses the Unix-only which command, causing it to always return false on Windows even when bd is properly installed. This issue was previously flagged and remains unaddressed.

🔎 Recommended fix (from previous review)
 async isBeadsInstalled(): Promise<boolean> {
   try {
-    const { stdout } = await execFileAsync('which', ['bd']);
-    return stdout.trim().length > 0;
+    // Try running 'bd --version' directly - works on all platforms
+    await execFileAsync('bd', ['--version']);
+    return true;
   } catch {
     return false;
   }
 }

This approach directly executes the bd command rather than relying on Unix-specific utilities.


343-368: Concurrent watching bug remains unresolved.

The shared instance variable this.watchTimeout (line 16) causes interference when multiple projects are watched simultaneously. If project A and project B are both watched, their debounce timeouts will overwrite each other, potentially dropping callbacks. This issue was previously flagged and remains unaddressed.

🔎 Recommended fix (from previous review)

Use a per-watcher local timeout variable instead of the shared instance variable:

 async watchDatabase(projectPath: string, callback: () => void): Promise<() => void> {
   const dbPath = this.getDatabasePath(projectPath);
+  let watchTimeout: NodeJS.Timeout | undefined;

   try {
     const watcher = fs.watch(dbPath, () => {
       // Debounce rapid changes
-      if (this.watchTimeout) {
-        clearTimeout(this.watchTimeout);
+      if (watchTimeout) {
+        clearTimeout(watchTimeout);
       }
-      this.watchTimeout = setTimeout(() => {
+      watchTimeout = setTimeout(() => {
         callback();
       }, 500);
     });

     // Return cleanup function
     return () => {
       watcher.close();
-      if (this.watchTimeout) {
-        clearTimeout(this.watchTimeout);
+      if (watchTimeout) {
+        clearTimeout(watchTimeout);
       }
     };
   } catch (error) {
     // If watching fails (e.g., database doesn't exist), return no-op cleanup
     return () => {};
   }
 }

Also remove the instance variable:

 export class BeadsService {
-  private watchTimeout?: NodeJS.Timeout;
🧹 Nitpick comments (2)
apps/server/src/services/beads-service.ts (2)

373-380: Consider more robust error detection.

The string-based error matching is functional but may be fragile if the bd CLI changes its error messages. If the CLI provides exit codes or structured error formats, consider using those for more reliable error classification.


1-11: Consider importing Beads types for better type safety.

The service uses any for issue objects and return types. Since the AI summary mentions that Beads-related types (BeadsIssue, BeadsStats, etc.) are defined in libs/types, consider importing and using them for improved type safety and better developer experience.

Example:

import type { BeadsIssue, BeadsStats, ListBeadsIssuesFilters } from '@repo/types';

Then update method signatures like:

async getIssue(projectPath: string, issueId: string): Promise<BeadsIssue>
async listIssues(projectPath: string, filters?: ListBeadsIssuesFilters): Promise<BeadsIssue[]>
async getStats(projectPath: string): Promise<BeadsStats>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c89e1ea and 0eceeee.

📒 Files selected for processing (2)
  • apps/server/src/services/beads-service.ts
  • apps/ui/src/lib/http-api-client.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/ui/src/lib/http-api-client.ts (2)
apps/ui/src/lib/electron.ts (1)
  • BeadsAPI (104-152)
libs/types/src/index.ts (3)
  • ListBeadsIssuesFilters (95-95)
  • CreateBeadsIssueInput (93-93)
  • UpdateBeadsIssueInput (94-94)
🔇 Additional comments (4)
apps/ui/src/lib/http-api-client.ts (2)

27-27: LGTM! Imports are correct.

The BeadsAPI type and related input/filter types are properly imported from their respective modules and align with the type definitions in the codebase.

Also applies to: 32-36


762-775: LGTM! Previous feedback has been addressed and verified.

The Beads API implementation correctly structures the request payloads:

  • create wraps the input under the issue key (Line 767), matching the server's expectation (apps/server/src/routes/beads/routes/create.ts:22)
  • update includes the updates key (Line 769), matching the server's expectation (apps/server/src/routes/beads/routes/update.ts:18)

The implementation follows the same pattern as other API groups in this file and the server-side route handlers properly validate and handle these payloads.

apps/server/src/services/beads-service.ts (2)

8-13: Good use of execFile for security.

The switch from exec to execFile with argument arrays throughout this service properly mitigates command injection vulnerabilities that were flagged in previous reviews.


93-306: CRUD operations properly secured.

All issue management methods (listIssues, createIssue, updateIssue, deleteIssue, dependency operations, etc.) correctly use execFileAsync with argument arrays, preventing shell injection. User-supplied values (titles, descriptions, filters) are safely passed as separate CLI arguments.

@0xtsotsi 0xtsotsi closed this Dec 24, 2025
@0xtsotsi 0xtsotsi reopened this Dec 24, 2025
Resolved conflicts in:
- apps/server/src/routes/beads/*: Kept change_logo branch (has JSDoc comments)
- apps/server/src/services/beads-service.ts: Kept change_logo branch (uses execFileAsync)
- apps/ui/src/components/views/beads-view/*: Kept change_logo branch (has JSDoc comments)
- apps/ui/src/store/app-store.ts: Kept main branch (has Claude Usage tracking)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts (1)

140-145: Add the missing beads keyboard shortcut and route file.

The code references shortcuts.beads on line 144, but this shortcut is not defined in DEFAULT_KEYBOARD_SHORTCUTS (libs/types/src/settings.ts). Additionally, no beads route file exists in the codebase.

You must:

  1. Add a beads entry to DEFAULT_KEYBOARD_SHORTCUTS in libs/types/src/settings.ts
  2. Create the corresponding beads route file in apps/ui/src/routes/

Without these, the navigation item will fail at runtime.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0eceeee and 2329458.

📒 Files selected for processing (3)
  • apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts
  • apps/ui/src/components/views/beads-view/beads-kanban-board.tsx
  • apps/ui/src/components/views/beads-view/components/beads-column.tsx
🔇 Additional comments (3)
apps/ui/src/components/views/beads-view/beads-kanban-board.tsx (1)

80-80: Previous issue successfully resolved.

The addition of the itemIds prop correctly addresses the past review comment about SortableContext requiring an array of IDs. The implementation properly extracts issue IDs from columnIssues and passes them to BeadsColumn for proper drag-and-drop functionality.

apps/ui/src/components/views/beads-view/components/beads-column.tsx (1)

18-18: Previous issue successfully resolved.

The implementation correctly addresses the past review feedback by:

  • Adding itemIds?: string[] to the component props interface (line 18)
  • Providing a safe default empty array (line 31)
  • Passing the ID array to SortableContext instead of children (line 80)

The SortableContext now receives the proper array of unique identifiers for drag-and-drop functionality to work correctly.

Also applies to: 31-31, 80-80

apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts (1)

50-54: LGTM! Clear and accurate documentation.

The JSDoc addition accurately describes the function's purpose and return value structure, improving code maintainability.

Fix TypeScript errors and merge conflicts introduced by Beads feature:
- Add missing getBdBin() export in beads common utilities
- Fix fs.watch() to use callback-based API from fs module
- Convert null to undefined for optional version properties
- Add 'beads' keyboard shortcut to KeyboardShortcuts interface
- Fix import paths in beads-view components (./hooks → ./beads-view/hooks)
- Add BeadsStats interface export for use-beads-column-issues hook
- Fix SortableContext items prop to accept string[] instead of ReactNode
- Resolve merge conflict markers in beads-view and beads-badges components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (10)
apps/ui/src/store/app-store.ts (1)

2664-2679: State initialization still missing for addBeadsIssue.

The early return when beadsByProject[projectPath] is undefined causes silent failures. This prevents issues from being added before the project's Beads state is explicitly initialized.

Based on past review comments, the recommended fix is to initialize the state when it doesn't exist:

🔎 Proposed fix
      addBeadsIssue: (projectPath, issue) => {
        const beadsByProject = get().beadsByProject;
-       const current = beadsByProject[projectPath];
-       if (!current) return;
+       const current = beadsByProject[projectPath] || {
+         issues: [],
+         readyWork: [],
+         lastUpdated: 0,
+         isWatching: false,
+       };

        set({
          beadsByProject: {
            ...beadsByProject,
            [projectPath]: {
              ...current,
              issues: [...current.issues, issue],
              lastUpdated: Date.now(),
            },
          },
        });
      },

The same fix should be applied to removeBeadsIssue at lines 2681-2696.

apps/server/src/routes/beads/routes/ready.ts (2)

23-26: Type assertion bypasses runtime validation.

The limit parameter has no runtime type checking. A client could send limit: "five" and it would be passed to the service layer as a string instead of a number, potentially causing runtime errors.

Consider implementing schema validation (e.g., Zod) to validate and coerce types at runtime:

🔎 Proposed fix using Zod
import { z } from 'zod';

const readySchema = z.object({
  projectPath: z.string(),
  limit: z.number().int().positive().optional(),
});

// In handler:
const parsed = readySchema.safeParse(req.body);
if (!parsed.success) {
  res.status(400).json({ success: false, error: 'Invalid request body' });
  return;
}
const { projectPath, limit } = parsed.data;

This same issue exists in update.ts and list.ts. Based on past review comments.


35-38: Service error details exposed to client.

Returning getErrorMessage(error) in the 500 response can leak implementation details such as file paths, stack traces, or internal service names to clients.

🔎 Proposed fix
 } catch (error) {
   logError(error, 'Get ready work failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to get ready work' });
 }

The full error details are still logged via logError for debugging. Based on past review comments.

apps/server/src/routes/beads/routes/update.ts (3)

18-29: Type assertion bypasses runtime validation.

The complex updates object with nested optional properties receives no runtime type validation. Invalid types (e.g., priority: "high" instead of a number) will be passed to the service layer, potentially causing runtime errors.

Consider using schema validation (e.g., Zod) to validate types at runtime. Based on past review comments, this is the same pattern issue identified across all Beads route handlers.


41-44: Empty updates object not rejected.

The validation only checks that updates is truthy, so an empty object {} passes and triggers a pointless service call with no actual changes.

🔎 Proposed fix
 if (!updates) {
   res.status(400).json({ success: false, error: 'updates are required' });
   return;
 }
+
+if (Object.keys(updates).length === 0) {
+  res.status(400).json({ success: false, error: 'updates cannot be empty' });
+  return;
+}

Based on past review comments.


48-51: Service error details exposed to client.

Returning getErrorMessage(error) in 500 responses can leak implementation details such as file paths, stack traces, or internal service names.

🔎 Proposed fix
 } catch (error) {
   logError(error, 'Update issue failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to update issue' });
 }

Based on past review comments.

apps/server/src/routes/beads/routes/list.ts (2)

23-35: Type assertion bypasses runtime validation.

The complex filters object with nested optional properties (arrays, numbers, strings) receives no runtime type validation. Invalid types (e.g., priorityMin: "high" instead of a number) will be passed to the service layer.

Based on past review comments, this is the same pattern issue identified across all Beads route handlers. Consider implementing schema validation (e.g., Zod) for runtime type safety.


44-47: Service error details exposed to client.

Returning getErrorMessage(error) in 500 responses can leak implementation details to clients.

🔎 Proposed fix
 } catch (error) {
   logError(error, 'List issues failed');
-  res.status(500).json({ success: false, error: getErrorMessage(error) });
+  res.status(500).json({ success: false, error: 'Failed to list issues' });
 }

Based on past review comments.

apps/server/src/services/beads-service.ts (2)

22-29: Duplicate: Cross-platform compatibility issue with which command.

This issue was already flagged in previous reviews. The which command is Unix-specific and won't work on Windows. Consider using a platform-agnostic approach like attempting to execute bd --version directly.

Based on past review comments.


344-369: Duplicate: Concurrent watching bug with shared timeout variable.

This issue was already flagged in previous reviews. The shared this.watchTimeout instance variable causes interference when multiple projects are watched simultaneously. Each watcher should have its own local timeout variable to avoid dropping callbacks.

Based on past review comments.

🧹 Nitpick comments (4)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (2)

82-101: Add error handling to ensure loading state is always cleared.

If the onCreate callback throws an exception, setIsCreating(false) at line 96 won't execute, leaving the button permanently disabled. Wrap the async call in a try-finally block to guarantee state cleanup.

🔎 Suggested fix
  const handleCreate = async () => {
    if (!title.trim()) {
      return;
    }

    setIsCreating(true);
-   const success = await onCreate({
-     title: title.trim(),
-     description: description.trim(),
-     type,
-     priority,
-     labels: labels.length > 0 ? labels : undefined,
-   });
-
-   setIsCreating(false);
-
-   if (success) {
-     onOpenChange(false);
+   try {
+     const success = await onCreate({
+       title: title.trim(),
+       description: description.trim(),
+       type,
+       priority,
+       labels: labels.length > 0 ? labels : undefined,
+     });
+
+     if (success) {
+       onOpenChange(false);
+     }
+   } finally {
+     setIsCreating(false);
    }
  };

198-204: Add accessible labels to improve screen reader support.

The label removal buttons lack accessible labels. Screen reader users won't know these buttons remove labels. Add an aria-label attribute to each button.

🔎 Suggested fix
                    <button
                      type="button"
                      onClick={() => handleRemoveLabel(label)}
                      className="rounded-full hover:bg-muted-foreground/20"
+                     aria-label={`Remove ${label} label`}
                    >
                      <X className="h-3 w-3" />
                    </button>
apps/server/src/routes/beads/routes/create.ts (1)

9-18: Add @param tag to JSDoc.

The JSDoc is missing a @param tag for the beadsService parameter. This would improve documentation completeness.

🔎 Proposed enhancement
 /**
  * Creates an Express request handler for creating a beads issue.
  *
  * The handler validates that `projectPath` and `issue.title` are present in the request body,
  * calls the provided service to create the issue, and sends a JSON response:
  * on success `{ success: true, issue }`, on validation failure a 400 with `{ success: false, error }`,
  * and on internal error a 500 with `{ success: false, error }`.
  *
+ * @param beadsService - The BeadsService instance used to create issues.
  * @returns An Express-compatible request handler that performs the described validation, creation, and responses.
  */
apps/server/src/services/beads-service.ts (1)

62-64: Optional: Simplify version coalescing.

The version ?? undefined pattern converts null to undefined. Since version is optional in the return type, you could simplify by directly assigning version: version || undefined or letting TypeScript handle the optional field naturally. This is a minor stylistic choice.

🔎 Possible simplification
       await fs.access(dbPath);
-      return { installed: true, initialized: true, version: version ?? undefined };
+      return { installed: true, initialized: true, ...(version && { version }) };
     } catch {
-      return { installed: true, initialized: false, version: version ?? undefined };
+      return { installed: true, initialized: false, ...(version && { version }) };
     }

Alternatively, keep the current approach if the explicit undefined is preferred for clarity.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2329458 and 3d19aa8.

📒 Files selected for processing (22)
  • apps/server/src/routes/beads/client/cli-wrapper.ts
  • apps/server/src/routes/beads/common.ts
  • apps/server/src/routes/beads/index.ts
  • apps/server/src/routes/beads/routes/create.ts
  • apps/server/src/routes/beads/routes/delete.ts
  • apps/server/src/routes/beads/routes/list.ts
  • apps/server/src/routes/beads/routes/ready.ts
  • apps/server/src/routes/beads/routes/update.ts
  • apps/server/src/routes/beads/routes/validate.ts
  • apps/server/src/services/beads-service.ts
  • apps/ui/src/components/ui/keyboard-map.tsx
  • apps/ui/src/components/views/beads-view.tsx
  • apps/ui/src/components/views/beads-view/beads-kanban-board.tsx
  • apps/ui/src/components/views/beads-view/components/beads-badges.tsx
  • apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts
  • apps/ui/src/store/app-store.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts
  • apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx
  • apps/server/src/routes/beads/common.ts
  • apps/ui/src/components/views/beads-view.tsx
  • apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx
  • apps/ui/src/components/views/beads-view/beads-kanban-board.tsx
  • apps/server/src/routes/beads/routes/delete.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts
  • apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts
  • apps/ui/src/components/views/beads-view/components/beads-badges.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
apps/server/src/routes/beads/client/cli-wrapper.ts (1)
apps/server/src/routes/beads/common.ts (1)
  • getBdBin (13-20)
apps/server/src/services/beads-service.ts (1)
apps/ui/scripts/update-version.mjs (1)
  • version (15-15)
🔇 Additional comments (13)
apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx (1)

41-48: LGTM! Clear and accurate documentation.

The JSDoc comment properly documents the component's purpose, props, and return value. The descriptions are clear and match the implementation.

apps/ui/src/components/ui/keyboard-map.tsx (1)

93-93: LGTM! Beads shortcut properly integrated.

The new beads shortcut is correctly added to both SHORTCUT_LABELS and SHORTCUT_CATEGORIES, following the same pattern as other navigation shortcuts. The integration is consistent with the default binding (beads: 'B') defined in app-store.ts.

Also applies to: 119-119

apps/ui/src/store/app-store.ts (1)

150-150: LGTM! Beads keyboard shortcut properly configured.

The beads shortcut is correctly added to the KeyboardShortcuts interface and assigned a default value of 'B', consistent with other navigation shortcuts in the system.

Also applies to: 183-183

apps/server/src/routes/beads/routes/ready.ts (1)

9-19: Good documentation addition.

The JSDoc block clearly describes the handler's behavior, expected inputs, and error handling patterns.

apps/server/src/routes/beads/index.ts (1)

15-20: LGTM! Clear API documentation.

The JSDoc block provides a clear overview of the router factory function and the available Beads endpoints.

apps/server/src/routes/beads/routes/update.ts (1)

9-14: Good documentation addition.

The JSDoc block clearly describes the update handler's purpose and behavior.

apps/server/src/routes/beads/routes/list.ts (1)

9-19: LGTM! Comprehensive documentation.

The JSDoc block clearly describes the expected request body structure, including all optional filter properties, and the response behavior.

apps/server/src/routes/beads/routes/validate.ts (1)

9-14: LGTM! Documentation added.

The JSDoc block clearly describes the validation handler's dual-mode behavior (global installation check vs. project-specific validation).

apps/server/src/routes/beads/client/cli-wrapper.ts (2)

30-34: LGTM! Async binary resolution properly implemented.

Converting runBd to async and awaiting getBdBin() centralizes the bd CLI path resolution. The function maintains its Promise<string> return type, ensuring backward compatibility for existing callers.


74-87: LGTM! Async binary resolution properly implemented.

Converting runBdExec to async and awaiting getBdBin() follows the same pattern as runBd. The function maintains its Promise<string> return type, preserving backward compatibility.

apps/server/src/routes/beads/routes/create.ts (1)

19-50: LGTM! Well-structured request handler.

The implementation correctly validates inputs, delegates to the service layer, and returns appropriate HTTP status codes. Error handling is comprehensive and follows best practices.

apps/server/src/services/beads-service.ts (2)

12-12: LGTM! Appropriate use of callback-based fs API.

Importing the callback-based fs module as fsCallback is the correct approach for using fs.watch, which isn't available in fs/promises. The naming clearly distinguishes it from the promises-based import.


270-271: Excellent security fixes! Command injection vulnerabilities resolved.

All the changes to use execFileAsync with argument arrays instead of string concatenation correctly address the critical command injection vulnerabilities flagged in previous reviews. User-provided values are now passed as separate arguments, preventing shell interpretation of special characters.

Based on past review comments that flagged command injection risks.

Also applies to: 282-283, 294-298, 314-314, 335-335

Copy link
Owner Author

Additional LSP Compatibility Fixes

This PR now includes fixes for TypeScript errors and merge conflicts that were introduced after the initial implementation:

Server-side fixes

  • ✅ Added missing getBdBin() export in beads common utilities
  • ✅ Fixed fs.watch() to use callback-based API from fs module
  • ✅ Converted null to undefined for optional version properties
  • ✅ Made CLI wrapper functions async to properly handle getBdBin()

UI fixes

  • ✅ Added 'beads' keyboard shortcut to all keyboard shortcut definitions
  • ✅ Fixed import paths in beads-view.tsx (changed from ./hooks/* to ./beads-view/hooks/*)
  • ✅ Added BeadsStats interface export for type compatibility
  • ✅ Fixed SortableContext to use itemIds array instead of ReactNode
  • ✅ Resolved merge conflict markers in beads-view.tsx and beads-badges.tsx

Quality Checks

✅ TypeScript: No errors
✅ ESLint: No errors
✅ Prettier: All files formatted

The changes ensure all code is compatible with the existing codebase structure and passes LSP diagnostics.

Fixed CRITICAL issues:
- Command injection vulnerability: Changed runBdExec() to use execFile()
  instead of shell-based exec() to prevent command injection
- Store state initialization: addBeadsIssue/removeBeadsIssue now initialize
  state if not present, preventing silent failures

Fixed IMPORTANT issues:
- Cross-platform compatibility: getBdBin() now uses 'where' on Windows
  and 'which' on Unix/Linux/macOS
- Watcher timeout bug: Changed from instance property to local variable
  to prevent race conditions when watching multiple databases
- Column categorization logic: Issues with blockers now correctly go to
  Blocked column regardless of their status (in_progress, open, etc.)

Technical changes:
- apps/server/src/routes/beads/client/cli-wrapper.ts: Use execFile with
  promisify for safe command execution
- apps/server/src/routes/beads/common.ts: Add platform detection for
  cross-platform binary lookup
- apps/server/src/services/beads-service.ts: Fix watchDatabase timeout scope
- apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts:
  Reorder conditions for correct column assignment
- apps/ui/src/store/app-store.ts: Initialize beads state in add/remove actions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Owner Author

PR Review Fixes - Security and Bug Fixes

I've addressed the CRITICAL and IMPORTANT issues identified in the PR review:

🔴 CRITICAL Issues Fixed

1. Command Injection Vulnerability (cli-wrapper.ts)

  • Changed runBdExec() from shell-based exec() to execFile() with argument arrays
  • Prevents command injection through malicious input

2. Store State Initialization (app-store.ts)

  • addBeadsIssue() and removeBeadsIssue() now initialize state if not present
  • Prevents silent failures when project state isn't initialized

🟡 IMPORTANT Issues Fixed

3. Cross-Platform Compatibility (common.ts)

  • getBdBin() now uses:
    • 'where' on Windows
    • 'which' on Unix/Linux/macOS
  • Previously only worked on Unix-like systems

4. Watcher Timeout Bug (beads-service.ts)

  • Changed watchTimeout from instance property to local variable
  • Prevents race conditions when multiple databases are watched concurrently

5. Column Categorization Logic (use-beads-column-issues.ts)

  • Issues with blockers now correctly go to Blocked column regardless of status
  • Previously, in_progress issues with blockers incorrectly went to In Progress column

✅ Quality Checks Passed

  • TypeScript compilation: ✅ No errors
  • ESLint: ✅ No new errors
  • Build: ✅ Successful

📝 Remaining

  • 16 NICE_TO_HAVE code cleanup items (unused imports, style improvements) - not blocking for merge

All fixes follow the clean-code.md guidelines and are ready for review.

@0xtsotsi 0xtsotsi merged commit 836ffb3 into main Dec 24, 2025
1 check passed
@0xtsotsi 0xtsotsi deleted the change_logo branch December 24, 2025 19:33
0xtsotsi pushed a commit that referenced this pull request Jan 6, 2026
Docstrings generation was requested by @0xtsotsi.

* #5 (comment)

The following files were modified:

* `apps/server/src/routes/beads/common.ts`
* `apps/server/src/routes/beads/index.ts`
* `apps/server/src/routes/beads/routes/create.ts`
* `apps/server/src/routes/beads/routes/delete.ts`
* `apps/server/src/routes/beads/routes/list.ts`
* `apps/server/src/routes/beads/routes/ready.ts`
* `apps/server/src/routes/beads/routes/update.ts`
* `apps/server/src/routes/beads/routes/validate.ts`
* `apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts`
* `apps/ui/src/components/views/beads-view.tsx`
* `apps/ui/src/components/views/beads-view/components/beads-badges.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/create-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/delete-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/dialogs/edit-issue-dialog.tsx`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-actions.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-column-issues.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-drag-drop.ts`
* `apps/ui/src/components/views/beads-view/hooks/use-beads-issues.ts`
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.

1 participant