Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import { createGitHubRoutes } from './routes/github/index.js';
import { createContextRoutes } from './routes/context/index.js';
import { createBeadsRoutes } from './routes/beads/index.js';
import { BeadsService } from './services/beads-service.js';
import { createReviewRoutes } from './routes/review/index.js';
import { ReviewWatcherService } from './services/review-watcher.js';
import { GitHubIssuePollerService } from './services/github-issue-poller-service.js';

// Load environment variables
Expand Down Expand Up @@ -168,6 +170,7 @@ const autoModeService = new AutoModeService(events);
const settingsService = new SettingsService(DATA_DIR);
const claudeUsageService = new ClaudeUsageService();
const beadsService = new BeadsService();
const reviewWatcherService = new ReviewWatcherService(events);
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:

# First, let's locate the ReviewWatcherService implementation
fd -t f -e ts -e tsx | xargs rg -l "class ReviewWatcherService" | head -5

Repository: 0xtsotsi/DevFlow

Length of output: 102


🏁 Script executed:

# Also check the context around line 171 in apps/server/src/index.ts
sed -n '160,185p' apps/server/src/index.ts

Repository: 0xtsotsi/DevFlow

Length of output: 992


🏁 Script executed:

# Search for any .start() or initialize() calls on reviewWatcherService
rg "reviewWatcherService\." apps/server/src/index.ts

Repository: 0xtsotsi/DevFlow

Length of output: 42


🏁 Script executed:

# Read the ReviewWatcherService implementation to understand initialization
cat -n apps/server/src/services/review-watcher.ts

Repository: 0xtsotsi/DevFlow

Length of output: 20015


🏁 Script executed:

# Search more broadly for any start() calls on the service after instantiation
rg "start\(\)" apps/server/src/index.ts -B 2 -A 2

Repository: 0xtsotsi/DevFlow

Length of output: 42


🏁 Script executed:

# Check if there are any lifecycle hooks or initialization patterns we might have missed
rg "reviewWatcherService" apps/server/src/index.ts -B 1 -A 1

Repository: 0xtsotsi/DevFlow

Length of output: 316


Add explicit .start() call to ReviewWatcherService after instantiation.

The ReviewWatcherService requires an explicit start() call to begin polling for in-review tasks. The constructor only initializes the service but does not start the polling loop. Without this call, the 30-second polling mechanism will never run. Add await reviewWatcherService.start(); in the async initialization block alongside agentService.initialize().

🤖 Prompt for AI Agents
In apps/server/src/index.ts around line 171, the ReviewWatcherService is
instantiated but never started; add an explicit await
reviewWatcherService.start() inside the async initialization block (alongside
agentService.initialize()) so the service's 30-second polling loop runs; ensure
the surrounding function is async and await the start call to handle any startup
errors.

const gitHubIssuePollerService = new GitHubIssuePollerService(events);

// Initialize services
Expand Down Expand Up @@ -216,7 +219,8 @@ app.use('/api/templates', apiLimiter, createTemplatesRoutes());
app.use('/api/terminal', apiLimiter, createTerminalRoutes());
app.use('/api/settings', strictLimiter, createSettingsRoutes(settingsService));
app.use('/api/claude', apiLimiter, createClaudeRoutes(claudeUsageService));
app.use('/api/github', apiLimiter, createGitHubRoutes(gitHubIssuePollerService));
app.use('/api/github', apiLimiter, createGitHubRoutes({ pollerService: gitHubIssuePollerService }));
app.use('/api/review', apiLimiter, createReviewRoutes(reviewWatcherService));
app.use('/api/context', apiLimiter, createContextRoutes());
app.use('/api/beads', beadsLimiter, createBeadsRoutes(beadsService));

Expand Down
31 changes: 26 additions & 5 deletions apps/server/src/routes/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,46 @@ import { Router } from 'express';
import { createCheckGitHubRemoteHandler } from './routes/check-github-remote.js';
import { createListIssuesHandler } from './routes/list-issues.js';
import { createListPRsHandler } from './routes/list-prs.js';
import { PRWatcherService } from '../../services/github-pr-watcher.js';
import { GitHubIssuePollerService } from '../../services/github-issue-poller-service.js';
import {
createStartAutoClaimHandler,
createStopAutoClaimHandler,
createGetAutoClaimStatusHandler,
} from './routes/auto-claim.js';
import {
createPRCommentHandler,
createPRCommentStatusHandler,
createTestWebhookHandler,
} from './routes/pr-comment-handler.js';

interface GitHubRoutesServices {
prWatcherService?: PRWatcherService;
pollerService?: GitHubIssuePollerService;
}

export function createGitHubRoutes(pollerService?: GitHubIssuePollerService): Router {
export function createGitHubRoutes(services?: GitHubRoutesServices): Router {
const router = Router();

router.post('/check-remote', createCheckGitHubRemoteHandler());
router.post('/issues', createListIssuesHandler());
router.post('/prs', createListPRsHandler());

// Webhook endpoints for PR comment monitoring (require prWatcherService)
if (services?.prWatcherService) {
router.post('/webhook/pr-comment', createPRCommentHandler(services.prWatcherService));
router.get(
'/webhook/pr-comment/status/:commentId',
createPRCommentStatusHandler(services.prWatcherService)
);
router.post('/webhook/test', createTestWebhookHandler());
}

// Auto-claim routes (require pollerService)
if (pollerService) {
router.post('/auto-claim/start', createStartAutoClaimHandler(pollerService));
router.post('/auto-claim/stop', createStopAutoClaimHandler(pollerService));
router.get('/auto-claim/status', createGetAutoClaimStatusHandler(pollerService));
if (services?.pollerService) {
router.post('/auto-claim/start', createStartAutoClaimHandler(services.pollerService));
router.post('/auto-claim/stop', createStopAutoClaimHandler(services.pollerService));
router.get('/auto-claim/status', createGetAutoClaimStatusHandler(services.pollerService));
}

return router;
Expand Down
18 changes: 18 additions & 0 deletions apps/server/src/routes/review/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Review routes - HTTP API for review watcher
*
* Provides endpoints to manage the review watcher service that monitors
* Vibe Kanban tasks in `inreview` status and auto-iterates on feedback.
*/

import { Router } from 'express';
import type { ReviewWatcherService } from '../../services/review-watcher.js';
import { createPendingReviewHandler } from './routes/pending-review.js';

export function createReviewRoutes(reviewWatcherService: ReviewWatcherService): Router {
const router = Router();

router.get('/pending', createPendingReviewHandler(reviewWatcherService));

return router;
Comment on lines +12 to +17
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for submit endpoint in review routes
rg -n "submit" --type=ts apps/server/src/routes/review/

# Check the complete contents of the review routes index file
cat -n apps/server/src/routes/review/index.ts

# Search for any createSubmitReviewHandler or similar
rg -n "createSubmitReviewHandler|/submit" --type=ts apps/server/src/

Repository: 0xtsotsi/DevFlow

Length of output: 789


🏁 Script executed:

# Search entire apps/server for /submit or submit handler
rg -n "/submit|submitReview|SubmitReview" --type=ts apps/server/

# Search for any PR description or documentation
fd -t f "README|DESCRIPTION|.md" apps/server/src/routes/review/ 2>/dev/null || echo "No markdown files found"

# Check if there's a pending-review handler to understand pattern
cat -n apps/server/src/routes/review/routes/pending-review.ts 2>/dev/null || echo "pending-review file not found"

# List all files in review routes directory
find apps/server/src/routes/review/ -type f 2>/dev/null

Repository: 0xtsotsi/DevFlow

Length of output: 1631


Implement missing /submit endpoint to complete PR objectives.

The /pending endpoint is correctly implemented with proper error handling and DI patterns. However, the /submit endpoint referenced in PR objectives is not present. Add it to this file following the same handler pattern as createPendingReviewHandler.

🤖 Prompt for AI Agents
In apps/server/src/routes/review/index.ts around lines 12 to 17, the router
lacks the required POST /submit endpoint referenced by the PR objectives; add a
route following the same pattern as the existing GET /pending by registering a
POST '/submit' handler that uses the injected ReviewWatcherService (e.g., call
createSubmitReviewHandler(reviewWatcherService)), return the router, and ensure
the new handler follows the same DI and error-handling conventions as
createPendingReviewHandler.

}
37 changes: 37 additions & 0 deletions apps/server/src/routes/review/routes/pending-review.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* GET /review/pending endpoint - List pending reviews
*
* Returns all tasks currently being watched by the review watcher service.
* This includes tasks in `inreview` status with their comment counts and iteration status.
*/

import type { Request, Response } from 'express';
import type { ReviewWatcherService } from '../../../services/review-watcher.js';
import { createLogger } from '@automaker/utils';

const logger = createLogger('Review');

export function createPendingReviewHandler(reviewWatcherService: ReviewWatcherService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const watchedTasks = reviewWatcherService.getWatchedTasks();

logger.info(`[Review] Fetched ${watchedTasks.length} pending reviews`);

res.json({
success: true,
data: {
tasks: watchedTasks,
count: watchedTasks.length,
isRunning: reviewWatcherService.getStatus().isRunning,
},
});
} catch (error) {
logger.error('[Review] Failed to fetch pending reviews:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : String(error),
});
}
};
}
17 changes: 4 additions & 13 deletions apps/server/src/services/github-issue-poller-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,7 @@ export class GitHubIssuePollerService {
}

// Check for claimable labels
const hasClaimableLabel = issue.labels.some((label) =>
CLAIMABLE_LABELS.includes(label.name)
);
const hasClaimableLabel = issue.labels.some((label) => CLAIMABLE_LABELS.includes(label.name));

if (!hasClaimableLabel) {
return false;
Expand Down Expand Up @@ -382,9 +380,7 @@ export class GitHubIssuePollerService {
// For now, we'll generate a mock task ID
const taskId = `task-${issue.number}-${Date.now()}`;

console.log(
`[GitHubPoller] Created Vibe Kanban task ${taskId} for issue #${issue.number}`
);
console.log(`[GitHubPoller] Created Vibe Kanban task ${taskId} for issue #${issue.number}`);

// TODO: Integrate with Vibe Kanban MCP when available
// const result = await mcp__vibe_kanban__create_task({
Expand Down Expand Up @@ -413,17 +409,12 @@ export class GitHubIssuePollerService {
/**
* Start a workspace session for the issue
*/
private async startWorkspaceSession(
issue: GitHubIssue,
taskId: string
): Promise<void> {
private async startWorkspaceSession(issue: GitHubIssue, taskId: string): Promise<void> {
if (!this.config) {
throw new Error('Poller not configured');
}

console.log(
`[GitHubPoller] Starting workspace session for issue #${issue.number}`
);
console.log(`[GitHubPoller] Starting workspace session for issue #${issue.number}`);

// Note: This is a placeholder - actual workspace startup would happen here
// TODO: Integrate with workspace session API when available
Expand Down
Loading