Initial curated projectbrain source from 8a7b1e0651dbdab97d744cbd2e653ab3970ac6c5
This commit is contained in:
commit
5aa3e8d685
700 files changed
+132801
No files matched your search
@@ -0,0 +1,367 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { prisma } from "../db/prisma.js";
|
||||
import type {
|
||||
ContextPackDto,
|
||||
ContextPackSummaryDto,
|
||||
CreateContextPackRequestDto,
|
||||
EvidenceRefDto,
|
||||
GeneratePromptRequestDto,
|
||||
GeneratedPromptDto,
|
||||
ProjectDna,
|
||||
} from "../types/api.js";
|
||||
|
||||
const toPublicId = (value: { slug: string | null; readableId: string | null; id: string }) =>
|
||||
value.slug ?? value.readableId ?? value.id;
|
||||
|
||||
const toJson = (value: unknown): Prisma.InputJsonValue =>
|
||||
JSON.parse(JSON.stringify(value)) as Prisma.InputJsonValue;
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null;
|
||||
|
||||
const asStringArray = (value: unknown): string[] =>
|
||||
Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
||||
|
||||
const asEvidenceRefs = (value: unknown): EvidenceRefDto[] =>
|
||||
Array.isArray(value) ? (value as EvidenceRefDto[]) : [];
|
||||
|
||||
type ContextPackMetadata = {
|
||||
dna?: ProjectDna;
|
||||
assumptions?: string[];
|
||||
constraints?: string[];
|
||||
evidence?: EvidenceRefDto[];
|
||||
signals?: string[];
|
||||
featureIds?: string[];
|
||||
bugIds?: string[];
|
||||
taskIds?: string[];
|
||||
decisionIds?: string[];
|
||||
memoryRuleIds?: string[];
|
||||
architectureNotes?: string[];
|
||||
regressions?: string[];
|
||||
risks?: string[];
|
||||
currentFocus?: ContextPackDto["currentFocus"];
|
||||
};
|
||||
|
||||
const toMetadata = (value: Prisma.JsonValue | null): ContextPackMetadata =>
|
||||
isRecord(value) ? (value as ContextPackMetadata) : {};
|
||||
|
||||
const findProjectRecordBySlugOrId = (projectIdOrSlug: string) =>
|
||||
prisma.project.findFirst({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
OR: [{ id: projectIdOrSlug }, { slug: projectIdOrSlug }],
|
||||
},
|
||||
include: {
|
||||
dna: true,
|
||||
},
|
||||
});
|
||||
|
||||
const toProjectDna = (project: NonNullable<Awaited<ReturnType<typeof findProjectRecordBySlugOrId>>>): ProjectDna => ({
|
||||
vision: project.dna?.vision ?? "",
|
||||
goals: asStringArray(project.dna?.goalsJson),
|
||||
nonGoals: asStringArray(project.dna?.nonGoalsJson),
|
||||
targetAudience: project.dna?.targetAudience ?? "",
|
||||
principles: asStringArray(project.dna?.principlesJson),
|
||||
successMetrics: Array.isArray(project.dna?.metricsJson) ? (project.dna?.metricsJson as ProjectDna["successMetrics"]) : [],
|
||||
maturityStage: (project.dna?.maturityStage as ProjectDna["maturityStage"]) ?? "Discovery",
|
||||
revision: project.dna?.revision ?? 1,
|
||||
updatedAt: project.dna?.updatedAt.toISOString() ?? project.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
type DbContextPackRecord = Awaited<ReturnType<typeof prisma.contextPack.findFirst>> & {
|
||||
sections?: Array<{
|
||||
id: string;
|
||||
slug: string | null;
|
||||
title: string;
|
||||
sectionType: string;
|
||||
orderIndex: number;
|
||||
contentJson: Prisma.JsonValue;
|
||||
metadataJson: Prisma.JsonValue | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
const toContextPackDto = async (
|
||||
contextPack: NonNullable<DbContextPackRecord>,
|
||||
): Promise<ContextPackDto> => {
|
||||
const project = await findProjectRecordBySlugOrId(contextPack.projectId);
|
||||
const metadata = toMetadata(contextPack.metadataJson);
|
||||
const quality = isRecord(contextPack.qualityJson) ? (contextPack.qualityJson as ContextPackDto["quality"]) : undefined;
|
||||
const trust = isRecord(contextPack.trustJson)
|
||||
? (contextPack.trustJson as ContextPackDto["trust"])
|
||||
: {
|
||||
source: "database context pack",
|
||||
confidence: contextPack.confidence,
|
||||
evidence: [],
|
||||
lastChecked: contextPack.updatedAt.toISOString(),
|
||||
approvalState: contextPack.approvalState,
|
||||
};
|
||||
|
||||
return {
|
||||
id: toPublicId(contextPack),
|
||||
projectId: project?.slug ?? contextPack.projectId,
|
||||
title: contextPack.title,
|
||||
category: contextPack.category as ContextPackDto["category"],
|
||||
dna: metadata.dna ?? (project ? toProjectDna(project) : {
|
||||
vision: "",
|
||||
goals: [],
|
||||
nonGoals: [],
|
||||
targetAudience: "",
|
||||
principles: [],
|
||||
successMetrics: [],
|
||||
maturityStage: "Discovery",
|
||||
revision: 0,
|
||||
updatedAt: contextPack.updatedAt.toISOString(),
|
||||
}),
|
||||
summary: contextPack.summary,
|
||||
assumptions: metadata.assumptions ?? [],
|
||||
constraints: metadata.constraints ?? [],
|
||||
evidence: metadata.evidence ?? [],
|
||||
signals: metadata.signals ?? [],
|
||||
featureIds: metadata.featureIds,
|
||||
bugIds: metadata.bugIds,
|
||||
taskIds: metadata.taskIds,
|
||||
decisionIds: metadata.decisionIds,
|
||||
memoryRuleIds: metadata.memoryRuleIds,
|
||||
architectureNotes: metadata.architectureNotes,
|
||||
regressions: metadata.regressions,
|
||||
risks: metadata.risks,
|
||||
quality,
|
||||
currentFocus: metadata.currentFocus,
|
||||
trust,
|
||||
generatedBy: contextPack.generatedBy as ContextPackDto["generatedBy"],
|
||||
createdAt: contextPack.createdAt.toISOString(),
|
||||
updatedAt: contextPack.updatedAt.toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
export const projectExists = async (projectIdOrSlug: string): Promise<boolean> =>
|
||||
Boolean(await findProjectRecordBySlugOrId(projectIdOrSlug));
|
||||
|
||||
export const listContextPacksForProject = async (
|
||||
projectIdOrSlug: string,
|
||||
): Promise<ContextPackDto[] | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
|
||||
const contextPacks = await prisma.contextPack.findMany({
|
||||
where: { projectId: project.id, deletedAt: null },
|
||||
orderBy: [{ updatedAt: "desc" }],
|
||||
include: { sections: { orderBy: { orderIndex: "asc" } } },
|
||||
});
|
||||
|
||||
return Promise.all(contextPacks.map(toContextPackDto));
|
||||
};
|
||||
|
||||
export const getContextPackBySlugOrId = async (
|
||||
projectIdOrSlug: string,
|
||||
contextPackIdOrSlug: string,
|
||||
): Promise<ContextPackDto | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
|
||||
const contextPack = await prisma.contextPack.findFirst({
|
||||
where: {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
OR: [
|
||||
{ id: contextPackIdOrSlug },
|
||||
{ slug: contextPackIdOrSlug },
|
||||
{ readableId: contextPackIdOrSlug },
|
||||
],
|
||||
},
|
||||
include: { sections: { orderBy: { orderIndex: "asc" } } },
|
||||
});
|
||||
|
||||
return contextPack ? toContextPackDto(contextPack) : undefined;
|
||||
};
|
||||
|
||||
export const getContextPackSummaryBySlugOrId = async (
|
||||
projectIdOrSlug: string,
|
||||
contextPackIdOrSlug: string,
|
||||
): Promise<ContextPackSummaryDto | undefined> => {
|
||||
const contextPack = await getContextPackBySlugOrId(projectIdOrSlug, contextPackIdOrSlug);
|
||||
if (!contextPack) return undefined;
|
||||
|
||||
return {
|
||||
projectId: contextPack.projectId,
|
||||
contextPackId: contextPack.id,
|
||||
title: contextPack.title,
|
||||
category: contextPack.category,
|
||||
summary: contextPack.summary,
|
||||
counts: {
|
||||
assumptions: contextPack.assumptions.length,
|
||||
constraints: contextPack.constraints.length,
|
||||
evidence: contextPack.evidence.length,
|
||||
features: contextPack.featureIds?.length ?? 0,
|
||||
bugs: contextPack.bugIds?.length ?? 0,
|
||||
tasks: contextPack.taskIds?.length ?? 0,
|
||||
decisions: contextPack.decisionIds?.length ?? 0,
|
||||
memoryRules: contextPack.memoryRuleIds?.length ?? 0,
|
||||
risks: contextPack.risks?.length ?? 0,
|
||||
},
|
||||
qualityScore: contextPack.quality?.score,
|
||||
createdAt: contextPack.createdAt,
|
||||
updatedAt: contextPack.updatedAt,
|
||||
};
|
||||
};
|
||||
|
||||
const toGeneratedBy = (sourceType: CreateContextPackRequestDto["sourceType"]): ContextPackDto["generatedBy"] => {
|
||||
if (sourceType === "check") return "check-run";
|
||||
if (sourceType === "manual") return "manual";
|
||||
return "user";
|
||||
};
|
||||
|
||||
export const createContextPackForProject = async (
|
||||
projectIdOrSlug: string,
|
||||
request: CreateContextPackRequestDto,
|
||||
): Promise<ContextPackDto | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
|
||||
const dna = toProjectDna(project);
|
||||
const features = await prisma.feature.findMany({
|
||||
where: { projectId: project.id, deletedAt: null },
|
||||
orderBy: { name: "asc" },
|
||||
});
|
||||
const evidence = await prisma.evidence.findMany({
|
||||
where: { projectId: project.id, deletedAt: null },
|
||||
orderBy: { lastSeen: "desc" },
|
||||
});
|
||||
const featureIds =
|
||||
request.includeFeatures === false
|
||||
? []
|
||||
: request.sourceType === "feature" && request.sourceId
|
||||
? [request.sourceId]
|
||||
: features.map((feature) => feature.slug ?? feature.readableId ?? feature.id);
|
||||
const evidenceRefs: EvidenceRefDto[] =
|
||||
request.includeEvidence === false
|
||||
? []
|
||||
: evidence.slice(0, 10).map((item) => ({
|
||||
id: `er-${item.slug ?? item.readableId ?? item.id}`,
|
||||
type: "analysis",
|
||||
refId: item.slug ?? item.readableId ?? item.id,
|
||||
label: item.reason,
|
||||
}));
|
||||
const createdAt = new Date();
|
||||
const slug = `cp-${project.slug}-${request.sourceType}-${createdAt.getTime().toString(36)}`;
|
||||
const title = request.title?.trim() || `${project.name} ${request.sourceType.replace("_", " ")} context pack`;
|
||||
const metadata: ContextPackMetadata = {
|
||||
dna,
|
||||
assumptions: request.sourceType === "manual" ? ["Manual context source requires human review."] : ["Database-generated context should be verified before use."],
|
||||
constraints: ["Context pack generated from database-backed project data."],
|
||||
evidence: evidenceRefs,
|
||||
signals: ["database-generated", request.sourceType],
|
||||
featureIds,
|
||||
memoryRuleIds: [],
|
||||
architectureNotes: request.includeArchitecture === false ? [] : [`Database-backed architecture context for ${project.name}.`],
|
||||
risks: request.includeRisks === false ? [] : ["Database-generated context still needs review before implementation."],
|
||||
regressions: request.includeRegressions === false ? [] : [],
|
||||
};
|
||||
const quality = {
|
||||
dnaIncluded: true,
|
||||
constitutionIncluded: false,
|
||||
constraintsIncluded: true,
|
||||
featuresIncluded: request.includeFeatures !== false,
|
||||
evidenceIncluded: request.includeEvidence !== false,
|
||||
risksIncluded: request.includeRisks !== false,
|
||||
memoryIncluded: request.includeMemoryRules !== false,
|
||||
doNotBreakIncluded: true,
|
||||
verificationIncluded: false,
|
||||
score: request.includeEvidence === false ? 58 : 74,
|
||||
};
|
||||
const trust = {
|
||||
source: "database context-pack create",
|
||||
confidence: request.includeEvidence === false ? 64 : 82,
|
||||
evidence: evidenceRefs,
|
||||
lastChecked: createdAt.toISOString(),
|
||||
approvalState: "pending" as const,
|
||||
};
|
||||
|
||||
const contextPack = await prisma.contextPack.create({
|
||||
data: {
|
||||
projectId: project.id,
|
||||
slug,
|
||||
readableId: slug,
|
||||
title,
|
||||
category: request.sourceType === "feature" ? "feature" : "quality",
|
||||
summary: `Created from database project ${project.slug} via ${request.sourceType} context source. DNA is always included.`,
|
||||
generatedBy: toGeneratedBy(request.sourceType),
|
||||
confidence: trust.confidence,
|
||||
approvalState: "pending",
|
||||
qualityJson: toJson(quality),
|
||||
trustJson: toJson(trust),
|
||||
metadataJson: toJson(metadata),
|
||||
createdBy: "runtime",
|
||||
updatedBy: "runtime",
|
||||
sourceType: "database_runtime",
|
||||
sections: {
|
||||
create: [
|
||||
{
|
||||
projectId: project.id,
|
||||
slug: "dna",
|
||||
title: "Project DNA",
|
||||
sectionType: "dna",
|
||||
orderIndex: 1,
|
||||
contentJson: toJson(dna),
|
||||
},
|
||||
{
|
||||
projectId: project.id,
|
||||
slug: "features",
|
||||
title: "Relevant features",
|
||||
sectionType: "features",
|
||||
orderIndex: 2,
|
||||
contentJson: toJson(featureIds),
|
||||
},
|
||||
{
|
||||
projectId: project.id,
|
||||
slug: "evidence",
|
||||
title: "Evidence references",
|
||||
sectionType: "evidence",
|
||||
orderIndex: 3,
|
||||
contentJson: toJson(evidenceRefs),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
include: { sections: { orderBy: { orderIndex: "asc" } } },
|
||||
});
|
||||
|
||||
return toContextPackDto(contextPack);
|
||||
};
|
||||
|
||||
export const generatePromptFromContextPack = async (
|
||||
projectIdOrSlug: string,
|
||||
request: GeneratePromptRequestDto,
|
||||
): Promise<GeneratedPromptDto | undefined> => {
|
||||
const contextPack = await getContextPackBySlugOrId(projectIdOrSlug, request.contextPackId);
|
||||
if (!contextPack) return undefined;
|
||||
|
||||
const createdAt = new Date().toISOString();
|
||||
const promptId = `prompt-${contextPack.projectId}-${Date.now().toString(36)}`;
|
||||
const expectedOutput = request.expectedOutput?.trim() || "Clear implementation notes and validation steps.";
|
||||
const generatedText = [
|
||||
`Generated from context pack ${contextPack.id} (${contextPack.title}).`,
|
||||
`Target tool: ${request.targetTool}.`,
|
||||
`Persona: ${request.persona}.`,
|
||||
`Objective: ${request.objective.trim()}.`,
|
||||
`Expected output: ${expectedOutput}`,
|
||||
`Context summary: ${contextPack.summary}`,
|
||||
`Project vision: ${contextPack.dna.vision}`,
|
||||
`Relevant features: ${(contextPack.featureIds ?? []).join(", ") || "none"}.`,
|
||||
`Risks: ${(contextPack.risks ?? []).join(" ") || "none listed"}.`,
|
||||
"Use only the supplied context pack facts and mark uncertainty explicitly.",
|
||||
].join("\n");
|
||||
|
||||
return {
|
||||
id: promptId,
|
||||
projectId: contextPack.projectId,
|
||||
contextPackId: contextPack.id,
|
||||
targetTool: request.targetTool,
|
||||
persona: request.persona,
|
||||
objective: request.objective.trim(),
|
||||
expectedOutput,
|
||||
generatedText,
|
||||
generatedFrom: "context-pack",
|
||||
createdAt,
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user