├── public ├── dashboard-dark.png └── dashboard-light.png ├── .github └── funding.yaml ├── .husky └── pre-commit ├── lib ├── constants │ ├── linear-config.ts │ └── buildware-config.ts ├── utils │ └── index.ts └── ai │ ├── estimate-claude-tokens.ts │ ├── limit-tokens.ts │ ├── calculate-llm-cost.ts │ ├── parse-ai-response.ts │ └── build-plan-prompt.ts ├── postcss.config.mjs ├── types ├── globals.d.ts ├── ai │ └── index.ts ├── github │ └── index.ts └── linear │ └── linear.ts ├── app ├── [workspaceId] │ ├── [projectId] │ │ ├── issues │ │ │ ├── loading.tsx │ │ │ ├── page.tsx │ │ │ ├── [issueId] │ │ │ │ ├── edit │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ └── create │ │ │ │ └── page.tsx │ │ ├── settings │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── templates │ │ │ ├── loading.tsx │ │ │ ├── [id] │ │ │ │ ├── page.tsx │ │ │ │ └── edit │ │ │ │ │ └── page.tsx │ │ │ ├── create │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── instructions │ │ │ ├── loading.tsx │ │ │ ├── page.tsx │ │ │ ├── create │ │ │ │ └── page.tsx │ │ │ └── [id] │ │ │ │ ├── page.tsx │ │ │ │ └── edit │ │ │ │ └── page.tsx │ │ ├── integrations │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ └── page.tsx │ ├── page.tsx │ ├── edit │ │ └── page.tsx │ └── layout.tsx ├── (auth) │ ├── login │ │ └── [[...login]] │ │ │ └── page.tsx │ ├── signup │ │ └── [[...signup]] │ │ │ └── page.tsx │ ├── layout.tsx │ └── onboarding │ │ └── page.tsx ├── (marketing) │ ├── layout.tsx │ └── page.tsx ├── api │ ├── auth │ │ └── callback │ │ │ ├── github │ │ │ └── route.ts │ │ │ └── linear │ │ │ └── route.ts │ └── linear │ │ └── webhook │ │ └── route.ts ├── layout.tsx ├── workspaces │ └── page.tsx └── globals.css ├── components ├── ui │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── collapsible.tsx │ ├── textarea.tsx │ ├── label.tsx │ ├── input.tsx │ ├── separator.tsx │ ├── progress.tsx │ ├── toaster.tsx │ ├── sonner.tsx │ ├── checkbox.tsx │ ├── slider.tsx │ ├── switch.tsx │ ├── badge.tsx │ ├── tooltip.tsx │ ├── hover-card.tsx │ ├── popover.tsx │ ├── avatar.tsx │ ├── toggle.tsx │ ├── radio-group.tsx │ ├── alert.tsx │ ├── scroll-area.tsx │ ├── resizable.tsx │ ├── toggle-group.tsx │ ├── tabs.tsx │ ├── button.tsx │ ├── card.tsx │ ├── accordion.tsx │ ├── input-otp.tsx │ ├── calendar.tsx │ └── multi-select.tsx ├── instructions │ ├── message-markdown-memoized.tsx │ ├── instruction.tsx │ ├── use-copy-to-clipboard.tsx │ ├── new-instruction-form.tsx │ ├── edit-instruction-form.tsx │ ├── instruction-list.tsx │ └── message-markdown.tsx ├── utility │ ├── theme-provider.tsx │ ├── not-found.tsx │ ├── loading-page.tsx │ └── theme-switcher.tsx ├── issues │ ├── issue-creation.tsx │ └── issues-list.tsx ├── dashboard │ └── reusable │ │ ├── edit-form.tsx │ │ ├── crud-page.tsx │ │ ├── data-list.tsx │ │ ├── data-item.tsx │ │ └── crud-form.tsx ├── profiles │ └── profile-creator.tsx ├── workspaces │ ├── edit-workspace-button.tsx │ └── create-workspace-button.tsx ├── templates │ ├── template.tsx │ ├── handle-save.tsx │ ├── template-select.tsx │ ├── template-list.tsx │ ├── edit-template-form.tsx │ └── new-template-form.tsx ├── integrations │ ├── integration.tsx │ ├── integrations.tsx │ ├── connect-github.tsx │ └── connect-linear.tsx ├── projects │ ├── create-project-button.tsx │ └── delete-project-button.tsx ├── marketing │ ├── main-section.tsx │ └── site-footer.tsx └── magicui │ ├── border-beam.tsx │ └── shine-border.tsx ├── db ├── migrations │ └── meta │ │ └── _journal.json ├── schema │ ├── index.ts │ ├── profiles-schema.ts │ ├── workspaces-schema.ts │ ├── issue-messages-schema.ts │ ├── embedded-branches-schema.ts │ ├── templates-schema.ts │ ├── issues-to-instructions-schema.ts │ ├── templates-to-instructions-schema.ts │ ├── issues-schema.ts │ ├── instructions-schema.ts │ ├── projects-schema.ts │ └── embedded-files-schema.ts ├── queries │ ├── index.ts │ ├── embedded-files-queries.ts │ ├── profiles-queries.ts │ ├── issues-queries.ts │ ├── issues-to-instructions-queries.ts │ ├── templates-to-instructions-queries.ts │ ├── instructions-queries.ts │ ├── workspaces-queries.ts │ ├── embedded-branches-queries.ts │ ├── issue-messages-queries.ts │ └── templates-queries.ts └── db.ts ├── drizzle.config.ts ├── scripts └── webhook-proxy │ └── proxy.ts ├── actions ├── auth │ └── auth.ts ├── linear │ ├── reactions.ts │ ├── webhook.ts │ └── issues.ts ├── github │ ├── tokenize-files.ts │ ├── list-branches.ts │ ├── auth.ts │ ├── delete-pr.ts │ ├── embed-files.ts │ ├── list-repos.ts │ ├── embed-branch.ts │ ├── embed-target-branch.ts │ ├── fetch-files.ts │ └── fetch-codebase.ts ├── ai │ ├── generate-embedding.ts │ └── generate-ai-response.ts └── retrieval │ └── get-similar-files.ts ├── next.config.mjs ├── components.json ├── .gitignore ├── tsconfig.json ├── .env.example ├── prettier.config.cjs ├── middleware.ts ├── license └── .eslintrc.json /public/dashboard-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-buildware-ai/main/public/dashboard-dark.png -------------------------------------------------------------------------------- /public/dashboard-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-buildware-ai/main/public/dashboard-light.png -------------------------------------------------------------------------------- /.github/funding.yaml: -------------------------------------------------------------------------------- 1 | # If you find my open-source work helpful, please consider sponsoring me! 2 | 3 | github: mckaywrigley 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | . "$(dirname -- "$0")/_/husky.sh" 4 | 5 | npm run lint:fix && npm run format:write && git add . 6 | -------------------------------------------------------------------------------- /lib/constants/linear-config.ts: -------------------------------------------------------------------------------- 1 | export const IN_PROGRESS_EMOJI = "thought_balloon" 2 | export const COMPLETED_EMOJI = "white_check_mark" 3 | 4 | export const AI_LABEL = "Assign To AI" 5 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /types/globals.d.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare global { 4 | interface CustomJwtSessionClaims { 5 | metadata: { 6 | onboardingComplete?: boolean 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/issues/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from "@/components/utility/loading-page" 2 | 3 | export default async function IssuesLoadingPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from "@/components/utility/loading-page" 2 | 3 | export default async function SettingsLoadingPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/templates/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from "@/components/utility/loading-page" 2 | 3 | export default async function TemplatesLoadingPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/instructions/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from "@/components/utility/loading-page" 2 | 3 | export default async function InstructionsLoadingPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/integrations/loading.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from "@/components/utility/loading-page" 2 | 3 | export default async function IntegrationsLoadingPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /app/(auth)/login/[[...login]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs" 2 | 3 | export default function LoginPage() { 4 | if (process.env.NEXT_PUBLIC_APP_MODE === "simple") { 5 | return null 6 | } 7 | 8 | return 9 | } 10 | -------------------------------------------------------------------------------- /app/(auth)/signup/[[...signup]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs" 2 | 3 | export default function SignUpPage() { 4 | if (process.env.NEXT_PUBLIC_APP_MODE === "simple") { 5 | return null 6 | } 7 | 8 | return 9 | } 10 | -------------------------------------------------------------------------------- /lib/ai/estimate-claude-tokens.ts: -------------------------------------------------------------------------------- 1 | import { encode } from "gpt-tokenizer" 2 | 3 | export function estimateClaudeSonnet3_5TokenCount(text: string): number { 4 | // Claude tokenizer is ~1.25-1.35 times more expensive than GPT, using 1.4 to be safe 5 | return encode(text).length * 1.4 6 | } 7 | -------------------------------------------------------------------------------- /db/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "7", 8 | "when": 1721203602599, 9 | "tag": "0000_burly_ben_urich", 10 | "breakpoints": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /types/ai/index.ts: -------------------------------------------------------------------------------- 1 | export interface AIFileInfo { 2 | path: string 3 | content: string 4 | language: string 5 | status: "new" | "modified" | "deleted" 6 | } 7 | 8 | export interface AIParsedResponse { 9 | fileList: string[] 10 | files: AIFileInfo[] 11 | prTitle: string 12 | } 13 | -------------------------------------------------------------------------------- /components/instructions/message-markdown-memoized.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from "react"; 2 | import ReactMarkdown, { Options } from "react-markdown"; 3 | 4 | export const MessageMarkdownMemoized: FC = memo(ReactMarkdown, (prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className); -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | interface AuthLayoutProps { 2 | children: React.ReactNode 3 | } 4 | 5 | export default async function AuthLayout({ children }: AuthLayoutProps) { 6 | return ( 7 | <> 8 |
9 | {children} 10 |
11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /components/utility/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 4 | import { ThemeProviderProps } from "next-themes/dist/types"; 5 | 6 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 7 | return {children}; 8 | } 9 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv" 2 | import { defineConfig } from "drizzle-kit" 3 | 4 | config({ path: ".env.local" }) 5 | 6 | export default defineConfig({ 7 | dialect: "postgresql", 8 | dbCredentials: { 9 | url: process.env.DATABASE_URL! 10 | }, 11 | schema: "./db/schema/index.ts", 12 | out: "./db/migrations" 13 | }) 14 | -------------------------------------------------------------------------------- /components/utility/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react" 2 | 3 | interface NotFoundProps { 4 | message: string 5 | } 6 | 7 | export const NotFound: FC = ({ message }) => { 8 | return ( 9 |
10 |
{message}
11 |
12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /components/issues/issue-creation.tsx: -------------------------------------------------------------------------------- 1 | import { getTemplatesWithInstructionsByProjectId } from "@/db/queries/templates-queries" 2 | import { NewIssueForm } from "./new-issue-form" 3 | 4 | export const IssueCreation = async ({ projectId }: { projectId: string }) => { 5 | const templates = await getTemplatesWithInstructionsByProjectId(projectId) 6 | 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /lib/constants/buildware-config.ts: -------------------------------------------------------------------------------- 1 | // text-embedding-3-small or text-embedding-3-large 2 | export const BUILDWARE_EMBEDDING_MODEL = "text-embedding-3-large" 3 | 4 | // Between 256 and 3072 5 | export const BUILDWARE_EMBEDDING_DIMENSIONS = 256 6 | 7 | // Max: 8192 8 | export const BUILDWARE_MAX_OUTPUT_TOKENS = 8192 9 | 10 | // Max: 200000 11 | export const BUILDWARE_MAX_INPUT_TOKENS = 200000 12 | -------------------------------------------------------------------------------- /components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /components/dashboard/reusable/edit-form.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react" 2 | 3 | interface EditFormProps { 4 | action: (formData: FormData) => Promise 5 | children: React.ReactNode 6 | } 7 | 8 | export const EditForm: FC = ({ action, children }) => { 9 | return ( 10 |
11 | {children} 12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /scripts/webhook-proxy/proxy.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | import SmeeClient from "smee-client" 3 | 4 | dotenv.config({ path: ".env.local" }) 5 | 6 | const port = process.env.PORT || 3000 7 | 8 | const smee = new SmeeClient({ 9 | source: process.env.WEBHOOK_URL!, 10 | target: `http://localhost:${port}/api/linear/webhook`, // LINEAR TEST 11 | logger: console 12 | }) 13 | 14 | const events = smee.start() 15 | // 16 | -------------------------------------------------------------------------------- /components/utility/loading-page.tsx: -------------------------------------------------------------------------------- 1 | import { Loader2 } from "lucide-react" 2 | import { FC } from "react" 3 | 4 | interface LoadingPageProps { 5 | size?: number 6 | } 7 | 8 | export const LoadingPage: FC = ({ size = 16 }) => { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/issues/page.tsx: -------------------------------------------------------------------------------- 1 | import { IssuesList } from "@/components/issues/issues-list" 2 | import { getIssuesByProjectId } from "@/db/queries/issues-queries" 3 | 4 | export const revalidate = 0 5 | 6 | export default async function IssuesPage({ 7 | params 8 | }: { 9 | params: { projectId: string } 10 | }) { 11 | const issues = await getIssuesByProjectId(params.projectId) 12 | 13 | return 14 | } 15 | -------------------------------------------------------------------------------- /actions/auth/auth.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { auth } from "@clerk/nextjs/server" 4 | 5 | const IS_SIMPLE_MODE = process.env.NEXT_PUBLIC_APP_MODE === "simple" 6 | const SIMPLE_USER_ID = "simple_user_1" 7 | 8 | export async function getUserId() { 9 | if (IS_SIMPLE_MODE) { 10 | return SIMPLE_USER_ID 11 | } 12 | 13 | const { userId } = auth() 14 | 15 | if (!userId) { 16 | throw new Error("User not authenticated") 17 | } 18 | 19 | return userId 20 | } 21 | -------------------------------------------------------------------------------- /actions/linear/reactions.ts: -------------------------------------------------------------------------------- 1 | import { LinearClient } from "@linear/sdk" 2 | 3 | export const createReaction = async ( 4 | linearClient: LinearClient, 5 | id: string, 6 | emoji: string, 7 | isComment = false 8 | ) => { 9 | try { 10 | return await linearClient.createReaction({ 11 | emoji, 12 | ...(isComment ? { commentId: id } : { issueId: id }) 13 | }) 14 | } catch (error) { 15 | console.error("Error creating reaction:", error) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | images: { 5 | remotePatterns: [ 6 | { 7 | protocol: "http", 8 | hostname: "localhost" 9 | }, 10 | { 11 | protocol: "http", 12 | hostname: "127.0.0.1" 13 | }, 14 | { 15 | protocol: "https", 16 | hostname: "**" 17 | } 18 | ] 19 | } 20 | } 21 | 22 | export default nextConfig 23 | -------------------------------------------------------------------------------- /db/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./embedded-branches-schema" 2 | export * from "./embedded-files-schema" 3 | export * from "./instructions-schema" 4 | export * from "./issue-messages-schema" 5 | export * from "./issues-schema" 6 | export * from "./issues-to-instructions-schema" 7 | export * from "./profiles-schema" 8 | export * from "./projects-schema" 9 | export * from "./templates-schema" 10 | export * from "./templates-to-instructions-schema" 11 | export * from "./workspaces-schema" 12 | -------------------------------------------------------------------------------- /app/[workspaceId]/page.tsx: -------------------------------------------------------------------------------- 1 | import { getWorkspaceById } from "@/db/queries/workspaces-queries" 2 | 3 | export const revalidate = 0 4 | 5 | export default async function WorkspacePage({ 6 | params 7 | }: { 8 | params: { workspaceId: string } 9 | }) { 10 | const { workspaceId } = params 11 | 12 | const workspaces = await getWorkspaceById(workspaceId) 13 | 14 | if (!workspaces) { 15 | return
Workspace not found
16 | } 17 | 18 | return
{workspaces.name}
19 | } 20 | -------------------------------------------------------------------------------- /db/queries/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./embedded-branches-queries" 2 | export * from "./embedded-files-queries" 3 | export * from "./instructions-queries" 4 | export * from "./issue-messages-queries" 5 | export * from "./issues-queries" 6 | export * from "./issues-to-instructions-queries" 7 | export * from "./profiles-queries" 8 | export * from "./projects-queries" 9 | export * from "./templates-queries" 10 | export * from "./templates-to-instructions-queries" 11 | export * from "./workspaces-queries" 12 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "magicui": "@/components/magicui" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { SiteFooter } from "@/components/marketing/site-footer" 2 | import { SiteHeader } from "@/components/marketing/site-header" 3 | 4 | interface MarketingLayoutProps { 5 | children: React.ReactNode 6 | } 7 | 8 | export default async function MarketingLayout({ 9 | children 10 | }: MarketingLayoutProps) { 11 | return ( 12 | <> 13 | 14 |
{children}
15 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/instructions/page.tsx: -------------------------------------------------------------------------------- 1 | import { InstructionsList } from "@/components/instructions/instruction-list" 2 | import { getInstructionsByProjectId } from "@/db/queries/instructions-queries" 3 | 4 | export const revalidate = 0 5 | 6 | export default async function InstructionPage({ 7 | params 8 | }: { 9 | params: { projectId: string } 10 | }) { 11 | const instructions = await getInstructionsByProjectId(params.projectId) 12 | 13 | return 14 | } 15 | -------------------------------------------------------------------------------- /db/schema/profiles-schema.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, text, timestamp } from "drizzle-orm/pg-core" 2 | 3 | export const profilesTable = pgTable("profiles", { 4 | userId: text("user_id").primaryKey(), 5 | createdAt: timestamp("created_at").notNull().defaultNow(), 6 | updatedAt: timestamp("updated_at") 7 | .notNull() 8 | .defaultNow() 9 | .$onUpdate(() => new Date()) 10 | }) 11 | 12 | export type InsertProfile = typeof profilesTable.$inferInsert 13 | export type SelectProfile = typeof profilesTable.$inferSelect 14 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/issues/[issueId]/edit/page.tsx: -------------------------------------------------------------------------------- 1 | import { EditIssueForm } from "@/components/issues/edit-issue-form" 2 | import { getIssueById } from "@/db/queries/issues-queries" 3 | 4 | export const revalidate = 0 5 | 6 | export default async function EditIssuePage({ 7 | params 8 | }: { 9 | params: { issueId: string } 10 | }) { 11 | const issue = await getIssueById(params.issueId) 12 | 13 | if (!issue) { 14 | return
Issue not found
15 | } 16 | 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/instructions/create/page.tsx: -------------------------------------------------------------------------------- 1 | import { CRUDPage } from "@/components/dashboard/reusable/crud-page" 2 | import NewInstructionForm from "@/components/instructions/new-instruction-form" 3 | 4 | export const revalidate = 0 5 | 6 | export default async function CreateInstructionPage() { 7 | return ( 8 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /actions/github/tokenize-files.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { GitHubFileContent } from "@/types/github" 4 | import { encode } from "gpt-tokenizer" 5 | 6 | export async function tokenizeFiles(filesContent: GitHubFileContent[]) { 7 | // Filter out files with more than 8k tokens and 0 tokens 8 | const tokenizedFiles = filesContent.filter( 9 | file => 10 | typeof file.content === "string" && 11 | encode(file.content).length <= 8000 && 12 | encode(file.content).length > 0 13 | ) 14 | 15 | return tokenizedFiles 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/issues/create/page.tsx: -------------------------------------------------------------------------------- 1 | import { CRUDPage } from "@/components/dashboard/reusable/crud-page" 2 | import { IssueCreation } from "@/components/issues/issue-creation" 3 | 4 | export const revalidate = 0 5 | 6 | export default async function CreateIssuePage({ 7 | params 8 | }: { 9 | params: { projectId: string } 10 | }) { 11 | return ( 12 | 17 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/page.tsx: -------------------------------------------------------------------------------- 1 | import { getProjectById } from "@/db/queries/projects-queries" 2 | 3 | export const revalidate = 0 4 | 5 | export default async function ProjectPage({ 6 | params 7 | }: { 8 | params: { projectId: string; workspaceId: string } 9 | }) { 10 | const project = await getProjectById(params.projectId) 11 | 12 | if (!project) { 13 | return
Project not found
14 | } 15 | 16 | return ( 17 |
18 |
{project.name}
19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /actions/github/list-branches.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { getAuthenticatedOctokit } from "./auth" 4 | 5 | export const listBranches = async ( 6 | installationId: number | null, 7 | repoFullName: string 8 | ): Promise => { 9 | try { 10 | const octokit = await getAuthenticatedOctokit(installationId) 11 | 12 | const [owner, repo] = repoFullName.split("/") 13 | const { data } = await octokit.repos.listBranches({ owner, repo }) 14 | 15 | return data.map(branch => branch.name) 16 | } catch (error: any) { 17 | console.error("Error fetching branches:", error) 18 | return [] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /components/instructions/instruction.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { deleteInstruction } from "@/db/queries/instructions-queries" 4 | import { SelectInstruction } from "@/db/schema" 5 | import { FC } from "react" 6 | import { InstructionTemplateView } from "../dashboard/reusable/instruction-template-view" 7 | 8 | interface InstructionsProps { 9 | instruction: SelectInstruction 10 | } 11 | 12 | export const Instruction: FC = ({ instruction }) => { 13 | return ( 14 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /app/[workspaceId]/[projectId]/templates/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import { Template } from "@/components/templates/template" 2 | import { NotFound } from "@/components/utility/not-found" 3 | import { getTemplateWithInstructionById } from "@/db/queries/templates-queries" 4 | 5 | export const revalidate = 0 6 | 7 | export default async function TemplatePage({ 8 | params 9 | }: { 10 | params: { id: string; projectId: string } 11 | }) { 12 | const template = await getTemplateWithInstructionById(params.id) 13 | 14 | if (!template) { 15 | return 16 | } 17 | 18 | return