feat: initial CodeBoard monorepo scaffold

Turborepo monorepo with npm workspaces:
- apps/web: Next.js 14 frontend with Tailwind v4, SSE progress, doc viewer
- apps/worker: BullMQ job processor (clone → parse → LLM generate)
- packages/shared: TypeScript types
- packages/parser: Babel-based AST parser (JS/TS) + regex (Python)
- packages/llm: OpenAI/Anthropic provider abstraction + prompt pipeline
- packages/diagrams: Mermaid architecture & dependency graph generators
- packages/database: Prisma schema (PostgreSQL)
- Docker multi-stage build (web + worker targets)

All packages compile successfully with tsc and next build.
This commit is contained in:
Vectry
2026-02-09 15:22:50 +00:00
parent efdc282da5
commit 79dad6124f
72 changed files with 10132 additions and 136 deletions

View File

@@ -0,0 +1,87 @@
import { simpleGit } from "simple-git";
import { mkdtemp, readdir, stat } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import type { CloneResult } from "@codeboard/shared";
async function countFiles(dir: string): Promise<{ files: number; lines: number }> {
let files = 0;
let lines = 0;
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === ".git" || entry.name === "node_modules") continue;
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
const sub = await countFiles(fullPath);
files += sub.files;
lines += sub.lines;
} else {
files++;
const fileStat = await stat(fullPath);
lines += Math.ceil(fileStat.size / 40);
}
}
return { files, lines };
}
export async function cloneRepository(repoUrl: string): Promise<CloneResult> {
const tmpDir = await mkdtemp(join(tmpdir(), "codeboard-"));
const git = simpleGit();
await git.clone(repoUrl, tmpDir, ["--depth", "1", "--single-branch"]);
const localGit = simpleGit(tmpDir);
const log = await localGit.log({ maxCount: 1 });
const lastCommit = log.latest?.hash ?? "unknown";
const repoName = repoUrl
.replace(/\.git$/, "")
.split("/")
.slice(-1)[0] ?? "unknown";
const { files: totalFiles, lines: totalLines } = await countFiles(tmpDir);
const languageCounts: Record<string, number> = {};
const extMap: Record<string, string> = {
".ts": "TypeScript", ".tsx": "TypeScript",
".js": "JavaScript", ".jsx": "JavaScript",
".py": "Python", ".go": "Go",
".rs": "Rust", ".java": "Java",
".rb": "Ruby", ".php": "PHP",
};
async function scanLanguages(dir: string) {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
await scanLanguages(fullPath);
} else {
const ext = entry.name.slice(entry.name.lastIndexOf("."));
const lang = extMap[ext];
if (lang) {
const fileStat = await stat(fullPath);
languageCounts[lang] = (languageCounts[lang] ?? 0) + fileStat.size;
}
}
}
}
await scanLanguages(tmpDir);
return {
localPath: tmpDir,
metadata: {
name: repoName,
description: "",
defaultBranch: "main",
languages: languageCounts,
stars: 0,
lastCommit,
totalFiles,
totalLines,
},
};
}

View File

@@ -0,0 +1,26 @@
import type { CodeStructure, GeneratedDocs } from "@codeboard/shared";
import { createProvider, generateDocumentation } from "@codeboard/llm";
export async function generateDocs(
codeStructure: CodeStructure,
onProgress?: (stage: string, progress: number) => void
): Promise<GeneratedDocs> {
const apiKey =
process.env.OPENAI_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? "";
if (!apiKey) {
throw new Error(
"No LLM API key configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY."
);
}
const providerType = process.env.OPENAI_API_KEY ? "openai" : "anthropic";
const provider = createProvider({
provider: providerType,
apiKey,
model: process.env.LLM_MODEL,
baseUrl: process.env.LLM_BASE_URL,
});
return generateDocumentation(codeStructure, provider, onProgress);
}

View File

@@ -0,0 +1,8 @@
import type { CodeStructure } from "@codeboard/shared";
import { analyzeRepository } from "@codeboard/parser";
export async function parseRepository(
localPath: string
): Promise<CodeStructure> {
return analyzeRepository(localPath);
}