refactoring the api endpoints to be separate files to reduce context usage

This commit is contained in:
Cody Seibert
2025-12-14 17:53:21 -05:00
parent cdc8334d82
commit 6b30271441
121 changed files with 4281 additions and 2927 deletions

View File

@@ -0,0 +1,21 @@
/**
* Common utilities for auto-mode routes
*/
import { createLogger } from "../../lib/logger.js";
const logger = createLogger("AutoMode");
/**
* Get error message from error object
*/
export function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : "Unknown error";
}
/**
* Log error details consistently
*/
export function logError(error: unknown, context: string): void {
logger.error(`${context}:`, error);
}

View File

@@ -0,0 +1,40 @@
/**
* Auto Mode routes - HTTP API for autonomous feature implementation
*
* Uses the AutoModeService for real feature execution with Claude Agent SDK
*/
import { Router } from "express";
import type { AutoModeService } from "../../services/auto-mode-service.js";
import { createStartHandler } from "./routes/start.js";
import { createStopHandler } from "./routes/stop.js";
import { createStopFeatureHandler } from "./routes/stop-feature.js";
import { createStatusHandler } from "./routes/status.js";
import { createRunFeatureHandler } from "./routes/run-feature.js";
import { createVerifyFeatureHandler } from "./routes/verify-feature.js";
import { createResumeFeatureHandler } from "./routes/resume-feature.js";
import { createContextExistsHandler } from "./routes/context-exists.js";
import { createAnalyzeProjectHandler } from "./routes/analyze-project.js";
import { createFollowUpFeatureHandler } from "./routes/follow-up-feature.js";
import { createCommitFeatureHandler } from "./routes/commit-feature.js";
export function createAutoModeRoutes(autoModeService: AutoModeService): Router {
const router = Router();
router.post("/start", createStartHandler(autoModeService));
router.post("/stop", createStopHandler(autoModeService));
router.post("/stop-feature", createStopFeatureHandler(autoModeService));
router.post("/status", createStatusHandler(autoModeService));
router.post("/run-feature", createRunFeatureHandler(autoModeService));
router.post("/verify-feature", createVerifyFeatureHandler(autoModeService));
router.post("/resume-feature", createResumeFeatureHandler(autoModeService));
router.post("/context-exists", createContextExistsHandler(autoModeService));
router.post("/analyze-project", createAnalyzeProjectHandler(autoModeService));
router.post(
"/follow-up-feature",
createFollowUpFeatureHandler(autoModeService)
);
router.post("/commit-feature", createCommitFeatureHandler(autoModeService));
return router;
}

View File

@@ -0,0 +1,35 @@
/**
* POST /analyze-project endpoint - Analyze project
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { createLogger } from "../../../lib/logger.js";
import { getErrorMessage, logError } from "../common.js";
const logger = createLogger("AutoMode");
export function createAnalyzeProjectHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath } = req.body as { projectPath: string };
if (!projectPath) {
res
.status(400)
.json({ success: false, error: "projectPath is required" });
return;
}
// Start analysis in background
autoModeService.analyzeProject(projectPath).catch((error) => {
logger.error(`[AutoMode] Project analysis error:`, error);
});
res.json({ success: true, message: "Project analysis started" });
} catch (error) {
logError(error, "Analyze project failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,37 @@
/**
* POST /commit-feature endpoint - Commit feature changes
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createCommitFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId } = req.body as {
projectPath: string;
featureId: string;
};
if (!projectPath || !featureId) {
res
.status(400)
.json({
success: false,
error: "projectPath and featureId are required",
});
return;
}
const commitHash = await autoModeService.commitFeature(
projectPath,
featureId
);
res.json({ success: true, commitHash });
} catch (error) {
logError(error, "Commit feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,37 @@
/**
* POST /context-exists endpoint - Check if context exists for a feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createContextExistsHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId } = req.body as {
projectPath: string;
featureId: string;
};
if (!projectPath || !featureId) {
res
.status(400)
.json({
success: false,
error: "projectPath and featureId are required",
});
return;
}
const exists = await autoModeService.contextExists(
projectPath,
featureId
);
res.json({ success: true, exists });
} catch (error) {
logError(error, "Check context exists failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,46 @@
/**
* POST /follow-up-feature endpoint - Follow up on a feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { createLogger } from "../../../lib/logger.js";
import { getErrorMessage, logError } from "../common.js";
const logger = createLogger("AutoMode");
export function createFollowUpFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId, prompt, imagePaths } = req.body as {
projectPath: string;
featureId: string;
prompt: string;
imagePaths?: string[];
};
if (!projectPath || !featureId || !prompt) {
res.status(400).json({
success: false,
error: "projectPath, featureId, and prompt are required",
});
return;
}
// Start follow-up in background
autoModeService
.followUpFeature(projectPath, featureId, prompt, imagePaths)
.catch((error) => {
logger.error(
`[AutoMode] Follow up feature ${featureId} error:`,
error
);
});
res.json({ success: true });
} catch (error) {
logError(error, "Follow up feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,44 @@
/**
* POST /resume-feature endpoint - Resume a feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { createLogger } from "../../../lib/logger.js";
import { getErrorMessage, logError } from "../common.js";
const logger = createLogger("AutoMode");
export function createResumeFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId, useWorktrees } = req.body as {
projectPath: string;
featureId: string;
useWorktrees?: boolean;
};
if (!projectPath || !featureId) {
res
.status(400)
.json({
success: false,
error: "projectPath and featureId are required",
});
return;
}
// Start resume in background
autoModeService
.resumeFeature(projectPath, featureId, useWorktrees ?? true)
.catch((error) => {
logger.error(`[AutoMode] Resume feature ${featureId} error:`, error);
});
res.json({ success: true });
} catch (error) {
logError(error, "Resume feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,44 @@
/**
* POST /run-feature endpoint - Run a single feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { createLogger } from "../../../lib/logger.js";
import { getErrorMessage, logError } from "../common.js";
const logger = createLogger("AutoMode");
export function createRunFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId, useWorktrees } = req.body as {
projectPath: string;
featureId: string;
useWorktrees?: boolean;
};
if (!projectPath || !featureId) {
res
.status(400)
.json({
success: false,
error: "projectPath and featureId are required",
});
return;
}
// Start execution in background
autoModeService
.executeFeature(projectPath, featureId, useWorktrees ?? true, false)
.catch((error) => {
logger.error(`[AutoMode] Feature ${featureId} error:`, error);
});
res.json({ success: true });
} catch (error) {
logError(error, "Run feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,31 @@
/**
* POST /start endpoint - Start auto mode loop
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createStartHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, maxConcurrency } = req.body as {
projectPath: string;
maxConcurrency?: number;
};
if (!projectPath) {
res
.status(400)
.json({ success: false, error: "projectPath is required" });
return;
}
await autoModeService.startAutoLoop(projectPath, maxConcurrency || 3);
res.json({ success: true });
} catch (error) {
logError(error, "Start auto loop failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,22 @@
/**
* POST /status endpoint - Get auto mode status
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createStatusHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const status = autoModeService.getStatus();
res.json({
success: true,
...status,
});
} catch (error) {
logError(error, "Get status failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,28 @@
/**
* POST /stop-feature endpoint - Stop a specific feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createStopFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { featureId } = req.body as { featureId: string };
if (!featureId) {
res
.status(400)
.json({ success: false, error: "featureId is required" });
return;
}
const stopped = await autoModeService.stopFeature(featureId);
res.json({ success: true, stopped });
} catch (error) {
logError(error, "Stop feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,19 @@
/**
* POST /stop endpoint - Stop auto mode loop
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createStopHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const runningCount = await autoModeService.stopAutoLoop();
res.json({ success: true, runningFeatures: runningCount });
} catch (error) {
logError(error, "Stop auto loop failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}

View File

@@ -0,0 +1,37 @@
/**
* POST /verify-feature endpoint - Verify a feature
*/
import type { Request, Response } from "express";
import type { AutoModeService } from "../../../services/auto-mode-service.js";
import { getErrorMessage, logError } from "../common.js";
export function createVerifyFeatureHandler(autoModeService: AutoModeService) {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath, featureId } = req.body as {
projectPath: string;
featureId: string;
};
if (!projectPath || !featureId) {
res
.status(400)
.json({
success: false,
error: "projectPath and featureId are required",
});
return;
}
const passes = await autoModeService.verifyFeature(
projectPath,
featureId
);
res.json({ success: true, passes });
} catch (error) {
logError(error, "Verify feature failed");
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}