Add beads: Skill v4 epic (1bg), Quality gates (n1h), Brainstorm epics (jq5, 2e6), memory nodes

This commit is contained in:
zenchantlive 2026-03-01 22:56:18 -08:00
parent 80c3d06284
commit 835018c183
6 changed files with 2189 additions and 136 deletions

View file

@ -0,0 +1,125 @@
import { NextResponse } from 'next/server';
import { deleteCommentViaDolt, updateCommentViaDolt } from '../../../../../../lib/read-interactions';
export interface RouteParams {
id: string;
commentId: string;
}
interface PatchBody {
projectRoot?: unknown;
text?: unknown;
}
interface CommentMutationDeps {
updateComment: (projectRoot: string, commentId: number, text: string) => Promise<boolean>;
deleteComment: (projectRoot: string, commentId: number) => Promise<boolean>;
}
const defaultDeps: CommentMutationDeps = {
updateComment: updateCommentViaDolt,
deleteComment: deleteCommentViaDolt,
};
function parseCommentId(raw: string): number {
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new Error('commentId must be a positive integer.');
}
return parsed;
}
function parseProjectRoot(raw: unknown): string {
if (typeof raw !== 'string' || !raw.trim()) {
throw new Error('projectRoot is required.');
}
return raw.trim();
}
function parseCommentText(raw: unknown): string {
if (typeof raw !== 'string' || !raw.trim()) {
throw new Error('text is required.');
}
return raw.trim();
}
function badRequest(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 400 });
}
function notFound(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 404 });
}
function serverError(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 500 });
}
export async function handlePatchCommentRequest(
request: Request,
params: RouteParams,
deps: CommentMutationDeps = defaultDeps,
): Promise<NextResponse> {
let body: PatchBody;
try {
body = (await request.json()) as PatchBody;
} catch {
return badRequest('Invalid JSON body.');
}
let projectRoot: string;
let commentId: number;
let text: string;
try {
projectRoot = parseProjectRoot(body.projectRoot);
commentId = parseCommentId(params.commentId);
text = parseCommentText(body.text);
} catch (error) {
return badRequest(error instanceof Error ? error.message : 'Invalid request.');
}
try {
const updated = await deps.updateComment(projectRoot, commentId, text);
if (!updated) {
return notFound('Comment not found.');
}
return NextResponse.json({ ok: true, id: params.id, commentId });
} catch (error) {
console.error('[API] Failed to update comment:', error);
return serverError('Failed to update comment.');
}
}
export async function handleDeleteCommentRequest(
request: Request,
params: RouteParams,
deps: CommentMutationDeps = defaultDeps,
): Promise<NextResponse> {
let projectRoot: string;
let commentId: number;
try {
projectRoot = parseProjectRoot(new URL(request.url).searchParams.get('projectRoot'));
commentId = parseCommentId(params.commentId);
} catch (error) {
return badRequest(error instanceof Error ? error.message : 'Invalid request.');
}
try {
const deleted = await deps.deleteComment(projectRoot, commentId);
if (!deleted) {
return notFound('Comment not found.');
}
return NextResponse.json({ ok: true, id: params.id, commentId });
} catch (error) {
console.error('[API] Failed to delete comment:', error);
return serverError('Failed to delete comment.');
}
}

View file

@ -1,141 +1,18 @@
import { NextResponse } from 'next/server';
import { deleteCommentViaDolt, updateCommentViaDolt } from '../../../../../../lib/read-interactions';
import type { NextResponse } from 'next/server';
import { handleDeleteCommentRequest, handlePatchCommentRequest, type RouteParams } from './comment-mutation';
export const dynamic = 'force-dynamic';
interface RouteParams {
id: string;
commentId: string;
}
interface PatchBody {
projectRoot?: unknown;
text?: unknown;
}
interface CommentMutationDeps {
updateComment: (projectRoot: string, commentId: number, text: string) => Promise<boolean>;
deleteComment: (projectRoot: string, commentId: number) => Promise<boolean>;
}
const defaultDeps: CommentMutationDeps = {
updateComment: updateCommentViaDolt,
deleteComment: deleteCommentViaDolt,
};
function parseCommentId(raw: string): number {
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new Error('commentId must be a positive integer.');
}
return parsed;
}
function parseProjectRoot(raw: unknown): string {
if (typeof raw !== 'string' || !raw.trim()) {
throw new Error('projectRoot is required.');
}
return raw.trim();
}
function parseCommentText(raw: unknown): string {
if (typeof raw !== 'string' || !raw.trim()) {
throw new Error('text is required.');
}
return raw.trim();
}
function badRequest(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 400 });
}
function notFound(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 404 });
}
function serverError(message: string): NextResponse {
return NextResponse.json({ ok: false, error: { message } }, { status: 500 });
}
export async function handlePatchCommentRequest(
request: Request,
params: RouteParams,
deps: CommentMutationDeps,
): Promise<NextResponse> {
let body: PatchBody;
try {
body = (await request.json()) as PatchBody;
} catch {
return badRequest('Invalid JSON body.');
}
let projectRoot: string;
let commentId: number;
let text: string;
try {
projectRoot = parseProjectRoot(body.projectRoot);
commentId = parseCommentId(params.commentId);
text = parseCommentText(body.text);
} catch (error) {
return badRequest(error instanceof Error ? error.message : 'Invalid request.');
}
try {
const updated = await deps.updateComment(projectRoot, commentId, text);
if (!updated) {
return notFound('Comment not found.');
}
return NextResponse.json({ ok: true, id: params.id, commentId });
} catch (error) {
console.error('[API] Failed to update comment:', error);
return serverError('Failed to update comment.');
}
}
export async function handleDeleteCommentRequest(
request: Request,
params: RouteParams,
deps: CommentMutationDeps,
): Promise<NextResponse> {
let projectRoot: string;
let commentId: number;
try {
projectRoot = parseProjectRoot(new URL(request.url).searchParams.get('projectRoot'));
commentId = parseCommentId(params.commentId);
} catch (error) {
return badRequest(error instanceof Error ? error.message : 'Invalid request.');
}
try {
const deleted = await deps.deleteComment(projectRoot, commentId);
if (!deleted) {
return notFound('Comment not found.');
}
return NextResponse.json({ ok: true, id: params.id, commentId });
} catch (error) {
console.error('[API] Failed to delete comment:', error);
return serverError('Failed to delete comment.');
}
}
export async function PATCH(
request: Request,
{ params }: { params: Promise<RouteParams> },
): Promise<NextResponse> {
return handlePatchCommentRequest(request, await params, defaultDeps);
return handlePatchCommentRequest(request, await params);
}
export async function DELETE(
request: Request,
{ params }: { params: Promise<RouteParams> },
): Promise<NextResponse> {
return handleDeleteCommentRequest(request, await params, defaultDeps);
return handleDeleteCommentRequest(request, await params);
}