SPB Git

spb/autoscholar

Public
2.7 KB · 84 lines typescript
1import { readdir, readFile } from "node:fs/promises";2import { join } from "node:path";3import { type Db, researchTemplates } from "@autoscholar/db";4import { eq } from "drizzle-orm";56const TEMPLATE_META: Record<string, { name: string; mode: string; description: string }> = {7  general: {8    name: "General research brief",9    mode: "empirical",10    description: "Discipline-agnostic starter covering every section a good research instruction needs.",11  },12  empirical: {13    name: "Empirical study",14    mode: "empirical",15    description: "Data-driven analysis with provenance, executed code, identification limits and robustness.",16  },17  theoretical: {18    name: "Theoretical result",19    mode: "theoretical",20    description: "Definitions, propositions, derivations, counterexample search and optional formal proof.",21  },22  computational: {23    name: "Computational / AI study",24    mode: "computational",25    description: "Executable benchmark with baselines, seeds, ablations and resource budgets.",26  },27  review: {28    name: "Literature review",29    mode: "review",30    description: "Systematic or narrative synthesis with search protocol, screening and verified references.",31  },32};3334/** Loads research/templates/<slug>/CLAUDE.md into the DB (idempotent; bumps version when content changes). */35export async function syncTemplates(db: Db, templatesDir: string): Promise<string[]> {36  const synced: string[] = [];37  let dirs: string[] = [];38  try {39    dirs = (await readdir(templatesDir, { withFileTypes: true }))40      .filter((d) => d.isDirectory() && TEMPLATE_META[d.name])41      .map((d) => d.name);42  } catch {43    return synced;44  }45  for (const slug of dirs) {46    let content: string;47    try {48      content = await readFile(join(templatesDir, slug, "CLAUDE.md"), "utf8");49    } catch {50      continue;51    }52    const meta = TEMPLATE_META[slug]!;53    const [existing] = await db54      .select()55      .from(researchTemplates)56      .where(eq(researchTemplates.slug, slug))57      .limit(1);58    if (!existing) {59      await db60        .insert(researchTemplates)61        .values({ slug, name: meta.name, mode: meta.mode, description: meta.description, content });62      synced.push(slug);63    } else if (existing.content !== content) {64      await db65        .update(researchTemplates)66        .set({67          content,68          version: existing.version + 1,69          updatedAt: new Date(),70          name: meta.name,71          description: meta.description,72          mode: meta.mode,73        })74        .where(eq(researchTemplates.id, existing.id));75      synced.push(slug);76    }77  }78  return synced;79}8081export async function listTemplates(db: Db) {82  return db.select().from(researchTemplates).orderBy(researchTemplates.slug);83}84
Files · ad78891b6b969902dedc372df227e1adc3cd93ba
Menu

Theme