Skip to content

Commit 9763bac

Browse files
authored
Merge pull request #53 from lak7/pacts-tracking
Added pacts for tracking bugs, tasks, and features within projects
2 parents b859b9d + 5895b4e commit 9763bac

266 files changed

Lines changed: 36266 additions & 8818 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## v0.3.0
4+
### Added
5+
- Pacts for tracking bugs, tasks, and features within projects
6+
- Manual pact creation and management
7+
38
## v0.2.0
49
### Added
510
- Automatic architecture regeneration on GitHub push events via webhooks
@@ -13,4 +18,4 @@
1318
- Initial open-source release
1419
- Spec-driven architecture generation
1520
- GitHub integration
16-
- Inngest background processing
21+
- Inngest background processing

actions/project.ts

Lines changed: 5 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { cache } from "react";
55
import { ChatOpenAI } from "@langchain/openai";
66
import { PromptTemplate } from "@langchain/core/prompts";
77
import { StringOutputParser } from "@langchain/core/output_parsers";
8-
import { generateEasyMediumPrompt, generateNthProjectPhase, generateProjectPlanDocs, initialDocsGenerationPrompt, ultraProjectChatBotPrompt } from "../prompts/ReverseArchitecture";
8+
import { ultraProjectChatBotPrompt } from "../prompts/ReverseArchitecture";
99
const openaiKey = process.env.OPENAI_API_KEY;
1010
const llm = new ChatOpenAI({
1111
openAIApiKey: openaiKey,
1212
model: "gpt-5-mini-2025-08-07"
1313
})
1414

15-
const llm2 = new ChatOpenAI({
15+
const llm2 = new ChatOpenAI({
1616
openAIApiKey: openaiKey,
1717
model: "gpt-5-nano-2025-08-07"
1818
})
@@ -24,8 +24,6 @@ export interface ProjectMessage {
2424
type: 'user' | 'assistant';
2525
prompt?: string;
2626
content: string;
27-
projectDocsId?: string;
28-
docsName?: string;
2927
timestamp: string;
3028
}
3129

@@ -434,17 +432,17 @@ export async function updateProjectMessages(projectId: string, messages: Project
434432
}
435433

436434

437-
export async function projectChatBot( userInput: string, projectFramework: string, conversationHistory: any[], projectArchitecture: any, projectAnalysis: string) {
435+
export async function projectChatBot( userInput: string, projectFramework: string, conversationHistory: any[], projectArchitecture: any): Promise<string | { error: string }> {
438436
const { userId } = await auth();
439-
if (!userId) {
437+
if (!userId) {
440438
return { error: 'Unauthorized' };
441439
}
442440

443441
// Format conversation history for the prompt
444442
const formattedHistory = conversationHistory.map(msg =>
445443
`${msg.type === 'user' ? 'User' : 'Assistant'}: ${msg.content}`
446444
).join('\n');
447-
445+
448446

449447
const prompt = PromptTemplate.fromTemplate(ultraProjectChatBotPrompt);
450448
const chain = prompt.pipe(llmWithWeb).pipe(new StringOutputParser());
@@ -458,86 +456,6 @@ export async function projectChatBot( userInput: string, projectFramework: strin
458456
return response;
459457
}
460458

461-
export async function generatePrompt(userInput: string, projectFramework: string, conversationHistory: any[], projectAnalysis: string) {
462-
const { userId } = await auth();
463-
if (!userId) {
464-
return { error: 'Unauthorized' };
465-
}
466-
// Format conversation history for the prompt
467-
const formattedHistory = conversationHistory.map(msg =>
468-
`${msg.type === 'user' ? 'User' : 'Assistant'}: ${msg.content}`
469-
).join('\n');
470-
471-
const prompt = PromptTemplate.fromTemplate(generateEasyMediumPrompt);
472-
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
473-
const response = await chain.invoke({
474-
userQuery: userInput,
475-
framework: projectFramework,
476-
projectAnalysis: projectAnalysis,
477-
conversationHistory: formattedHistory
478-
});
479-
return response;
480-
}
481-
482-
export async function initialDocsGeneration(userInput: string, projectFramework: string, conversationHistory: any[], projectAnalysis: string) {
483-
const { userId } = await auth();
484-
if (!userId) {
485-
return { error: 'Unauthorized' };
486-
}
487-
// Format conversation history for the prompt
488-
const formattedHistory = conversationHistory.map(msg =>
489-
`${msg.type === 'user' ? 'User' : 'Assistant'}: ${msg.content}`
490-
).join('\n');
491-
492-
const prompt = PromptTemplate.fromTemplate(initialDocsGenerationPrompt);
493-
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
494-
const response = await chain.invoke({
495-
userQuery: userInput,
496-
framework: projectFramework,
497-
projectAnalysis: projectAnalysis,
498-
conversationHistory: formattedHistory
499-
});
500-
return response;
501-
}
502-
503-
// Create project context docs
504-
export async function createProjectContextDocs(
505-
projectChatId: any,
506-
contextName: string,
507-
summarizedContext: string,
508-
projectRules?: string,
509-
humanReview?: string,
510-
plan?: string,
511-
phases?: any[],
512-
phaseCount?: number
513-
) {
514-
const { userId } = await auth();
515-
if (!userId) {
516-
return { error: 'Unauthorized' };
517-
}
518-
519-
try {
520-
521-
// Create new project context docs
522-
const projectContextDocs = await db.projectContextDocs.create({
523-
data: {
524-
projectChatId,
525-
contextName,
526-
summarizedContext,
527-
projectRules,
528-
humanReview,
529-
plan,
530-
phases: phases as any,
531-
phaseCount
532-
}
533-
});
534-
535-
return { success: true, projectContextDocs };
536-
} catch (error) {
537-
console.error("Error creating project context docs:", error);
538-
return { error: 'Failed to create project context docs' };
539-
}
540-
}
541459

542460
// Get project context docs by ID
543461
export async function getProjectContextDocs(projectChatId: any) {
@@ -566,51 +484,6 @@ export async function getProjectContextDocs(projectChatId: any) {
566484
}
567485
}
568486

569-
export async function generateProjectPlan( framework: string, phaseCount: string, detailedAnalysis: string, requirement: string) {
570-
const { userId } = await auth();
571-
if (!userId) {
572-
return { error: 'Unauthorized' };
573-
}
574-
575-
const prompt = PromptTemplate.fromTemplate(generateProjectPlanDocs);
576-
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
577-
const response = await chain.invoke({
578-
framework: framework,
579-
phaseCount: phaseCount,
580-
projectAnalysis: detailedAnalysis,
581-
requirement: requirement
582-
});
583-
return response;
584-
}
585-
586-
export async function generateNthPhase(plan: string, framework: string, requirement: string, phaseNum: string) {
587-
const { userId } = await auth();
588-
if (!userId) {
589-
return { error: 'Unauthorized' };
590-
}
591-
592-
const prompt = PromptTemplate.fromTemplate(generateNthProjectPhase);
593-
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
594-
const response = await chain.invoke({
595-
plan: plan,
596-
framework: framework,
597-
requirement: requirement,
598-
phaseNum: phaseNum
599-
});
600-
return response;
601-
}
602-
603-
export async function updateProjectContextDocs(projectContextDocsId: string, plan: string, phases: any[]) {
604-
const { userId } = await auth();
605-
if (!userId) {
606-
return { error: 'Unauthorized' };
607-
}
608-
609-
await db.projectContextDocs.update({
610-
where: {id: projectContextDocsId},
611-
data: {plan: plan, phases: phases as any}
612-
})
613-
}
614487

615488
// System-level helper for Inngest to save the initial assistant message without requiring auth
616489
export async function saveInitialMessageForInngestRevArchitecture(

actions/project/pacts.ts

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"use server";
2+
import { db } from "@/lib/db";
3+
import { auth } from "@clerk/nextjs/server";
4+
import { cache } from "react";
5+
6+
export type PactType = 'BUG' | 'TASK' | 'FEATURE';
7+
export type PactStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED';
8+
9+
export interface Pact {
10+
id: string;
11+
projectId: string;
12+
type: PactType;
13+
status: PactStatus;
14+
head: string;
15+
body: any;
16+
githubIssue: any;
17+
createdAt: Date;
18+
updatedAt: Date;
19+
}
20+
21+
export async function createPact(
22+
projectId: string,
23+
type: PactType,
24+
head: string,
25+
body?: any
26+
) {
27+
const { userId } = await auth();
28+
if (!userId) {
29+
return { error: 'Unauthorized' };
30+
}
31+
32+
// Verify the project belongs to the user
33+
const project = await db.project.findUnique({
34+
where: { id: projectId, userId: userId },
35+
select: { id: true }
36+
});
37+
38+
if (!project) {
39+
return { error: 'Project not found or unauthorized' };
40+
}
41+
42+
try {
43+
const pact = await db.pact.create({
44+
data: {
45+
projectId,
46+
type,
47+
head,
48+
body: body || null,
49+
status: 'PENDING',
50+
}
51+
});
52+
53+
return { success: true, pact };
54+
} catch (error) {
55+
console.error('Error creating pact:', error);
56+
return { error: 'Failed to create pact' };
57+
}
58+
}
59+
60+
export async function updatePact(
61+
pactId: string,
62+
head: string,
63+
body?: any
64+
) {
65+
const { userId } = await auth();
66+
if (!userId) {
67+
return { error: 'Unauthorized' };
68+
}
69+
70+
// Verify the pact belongs to user's project
71+
const pact = await db.pact.findUnique({
72+
where: { id: pactId },
73+
include: { project: { select: { userId: true } } }
74+
});
75+
76+
if (!pact || pact.project.userId !== userId) {
77+
return { error: 'Pact not found or unauthorized' };
78+
}
79+
80+
try {
81+
const updatedPact = await db.pact.update({
82+
where: { id: pactId },
83+
data: {
84+
head,
85+
body: body || null,
86+
updatedAt: new Date()
87+
}
88+
});
89+
90+
return { success: true, pact: updatedPact };
91+
} catch (error) {
92+
console.error('Error updating pact:', error);
93+
return { error: 'Failed to update pact' };
94+
}
95+
}
96+
97+
/**
98+
* Update pact status
99+
*/
100+
export async function updatePactStatus(
101+
pactId: string,
102+
status: PactStatus
103+
) {
104+
const { userId } = await auth();
105+
if (!userId) {
106+
return { error: 'Unauthorized' };
107+
}
108+
109+
// Verify the pact belongs to user's project
110+
const pact = await db.pact.findUnique({
111+
where: { id: pactId },
112+
include: { project: { select: { userId: true } } }
113+
});
114+
115+
if (!pact || pact.project.userId !== userId) {
116+
return { error: 'Pact not found or unauthorized' };
117+
}
118+
119+
try {
120+
const updatedPact = await db.pact.update({
121+
where: { id: pactId },
122+
data: {
123+
status,
124+
updatedAt: new Date()
125+
}
126+
});
127+
128+
return { success: true, pact: updatedPact };
129+
} catch (error) {
130+
console.error('Error updating pact status:', error);
131+
return { error: 'Failed to update pact status' };
132+
}
133+
}
134+
135+
136+
export async function deletePact(pactId: string) {
137+
const { userId } = await auth();
138+
if (!userId) {
139+
return { error: 'Unauthorized' };
140+
}
141+
142+
const pact = await db.pact.findUnique({
143+
where: { id: pactId },
144+
include: { project: { select: { userId: true } } }
145+
});
146+
147+
if (!pact || pact.project.userId !== userId) {
148+
return { error: 'Pact not found or unauthorized' };
149+
}
150+
151+
try {
152+
await db.pact.delete({
153+
where: { id: pactId }
154+
});
155+
156+
return { success: true };
157+
} catch (error) {
158+
console.error('Error deleting pact:', error);
159+
return { error: 'Failed to delete pact' };
160+
}
161+
}
162+
163+
export const getPactsByProject = cache(async (projectId: string, type?: PactType) => {
164+
const { userId } = await auth();
165+
if (!userId) {
166+
return { error: 'Unauthorized' };
167+
}
168+
169+
try {
170+
const pacts = await db.pact.findMany({
171+
where: {
172+
projectId,
173+
...(type && { type })
174+
},
175+
orderBy: {
176+
createdAt: 'desc'
177+
}
178+
});
179+
180+
return { success: true, pacts };
181+
} catch (error) {
182+
console.error('Error fetching pacts:', error);
183+
return { error: 'Failed to fetch pacts' };
184+
}
185+
});

0 commit comments

Comments
 (0)