Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions kits/prd-copilot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Flow Configurations
PRD_COPILOT_FLOW_ID=your-lamatic-flow-uuid-here

# API Credentials
LAMATIC_API_KEY=your-lamatic-client-api-key-here
LAMATIC_PROJECT_ID=your-lamatic-project-id-here
LAMATIC_API_URL=https://your-organization-project-dev.lamatic.dev
104 changes: 104 additions & 0 deletions kits/prd-copilot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# PRD Copilot Kit

An AI-powered Product Requirement Document (PRD) generator that drafts feature specs, user personas, edge cases, and visual Mermaid.js flowcharts through an interactive refinement loop.

---

## 🧭 Step-by-Step Lamatic Studio Setup

Since PRD Copilot uses Lamatic's serverless orchestration, you need to create the flow in Lamatic Studio first. Follow this guide to build the flow:

### 1. Create a New Project in Lamatic Studio
1. Go to [studio.lamatic.ai](https://studio.lamatic.ai) and sign in.
2. Click **Create Project +**.
3. Name your project (e.g. `PRD Copilot`) and complete creation.

### 2. Design the GraphQL/API Flow
Create a new blank Flow in your project and name it `prd-copilot`:

1. **Trigger Node**:
* Add an **API Request (GraphQL)** node.
* Add three inputs to its schema:
* `mode` (string): Either `"draft"` or `"refine"`.
* `instructions` (string): The raw user idea (in draft) or the original draft PRD (in refine).
* `answers` (string, optional): The user's answers to the questions (in refine).
2. **Condition Node**:
* Inspect the `mode` input (`{{triggerNode_1.output.mode}}`).
* Route to **Condition 1 (Draft)** if `mode == "draft"`.
* Route to **Condition 2 (Refine)** if `mode == "refine"`.
3. **Draft Branch (Condition 1)**:
* Add an **LLM Node**.
* System Prompt:
```
You are a Product Management Assistant. Based on the user's raw product idea, write a structured initial PRD outline in markdown containing:
- Core Value Proposition
- User Personas (Target Audience)
- Core Features List
- Critical Edge Cases to Consider

Additionally, identify 3-4 key clarification questions that are absolutely vital to ask the user before you can build a final specification. Output the result in JSON format:
{
"prd": "markdown_outline_string",
"questions": ["question 1", "question 2", "question 3"],
"mermaid": ""
}
Ensure you return a valid JSON object. Do not add raw text comments outside the JSON.
```
* User Prompt:
```
Raw Idea: {{triggerNode_1.output.instructions}}
```
4. **Refine Branch (Condition 2)**:
* Add an **LLM Node**.
* System Prompt:
```
You are a Senior Product Manager. Take the original PRD draft and incorporate the user's answers to the clarifying questions. Generate:
1. A finalized, highly detailed PRD in markdown format.
2. A Mermaid.js flowchart code representing the application's user flow.

Output the result in JSON format:
{
"prd": "final_markdown_prd_string",
"questions": [],
"mermaid": "graph TD; ... (valid Mermaid syntax)"
}
Ensure you return a valid JSON object. Do not add raw text comments outside the JSON.
```
* User Prompt:
```
Original Draft: {{triggerNode_1.output.instructions}}
User Answers: {{triggerNode_1.output.answers}}
```
5. **Finalize Output Node**:
* Add a **Code Node** or merge the branch outputs into a single JSON structure.
6. **API Response Node**:
* Set the GraphQL output mapping to return `{{finaliseNode.output}}` as the response `answer`.
7. Click **Deploy** in the top right corner.

---

## 🚀 How to Run Locally

Once you have deployed the flow, configure the local Next.js client to test it:

### 1. Copy Environment Variables
Navigate to the app folder and create a `.env.local` file:
```bash
cd kits/prd-copilot/apps
cp .env.example .env.local
```

### 2. Enter API Credentials
Open `.env.local` and paste your keys from Lamatic Studio:
* **`LAMATIC_API_KEY`**: Obtain from **Settings → API Keys** in Studio.
* **`LAMATIC_PROJECT_ID`**: Obtain from **Settings → Project** in Studio.
* **`LAMATIC_API_URL`**: Obtain from the Endpoint URL in **Settings → API Docs**.
* **`PRD_COPILOT_FLOW_ID`**: Open your deployed `prd-copilot` flow, click the three-dot details menu, and copy the **Flow ID**.

### 3. Install Dependencies & Boot the App
Run these commands inside `kits/prd-copilot/apps`:
```bash
npm install
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) in your browser to start generating PRDs!
64 changes: 64 additions & 0 deletions kits/prd-copilot/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# PRD Copilot Agent

## Overview
This project solves the problem of drafting comprehensive, detail-oriented Product Requirement Documents (PRDs) and visual user flowcharts. Often, teams write a simple list of features but miss edge cases, user personas, API requirements, and technical constraints.

PRD Copilot implements a **single-flow** AgentKit pipeline that routes requests by mode ("draft" or "refine") and then orchestrates multiple model calls to create a PRD, ask clarifying questions, and generate a Mermaid.js user flowchart from user feedback.

---

## Purpose
The goal of this agent system is to provide an interactive, structured content-generation endpoint that turns a simple product idea into a complete PRD.

After it runs, the caller receives a polished Markdown PRD, a list of clarifying questions to refine details, and a Mermaid.js diagram definition representing the application flow.

## Flows

### `1. PRD Copilot - Generate & Refine`

- **Flow ID / Env key mapping:** `prd-copilot` (configured via `PRD_COPILOT_FLOW_ID`)

#### Trigger
- **Invocation type:** API request via a GraphQL trigger node (`API Request (graphqlNode)`).
- **Expected input shape:**
- `mode` (string): `"draft"` (for initial idea submission) or `"refine"` (for updating with answers to clarifying questions).
- `instructions` (string): The raw product description (used in `draft` mode) or the original draft PRD (used in `refine` mode).
- `answers` (string, optional): The user's answers to the clarifying questions (used in `refine` mode).

#### What it does
1. **API Request**: Receives inputs (`mode`, `instructions`, `answers`).
2. **Condition**: Routes based on `mode`.
- **`draft` Branch**:
- Sends the raw idea to the LLM.
- Generates user personas, feature specs, and edge cases.
- Generates 3-4 clarifying questions to pinpoint missing requirements.
- **`refine` Branch**:
- Takes the original draft PRD + user answers.
- Incorporates the answers to finalize the PRD.
- Generates a Mermaid.js user flowchart representing the application flow.
3. **Parse JSON**: Normalizes the LLM responses (handling PRD markdown and Mermaid code blocks separately).
4. **Finalise Output**: Formats a clean JSON payload for the API response.
5. **API Response**: Returns the payload.

#### Output
- **Success response:** a JSON response containing:
- `prd`: Markdown string of the PRD draft or final PRD.
- `questions`: Array of questions (in `draft` mode) or empty array (in `refine` mode).
- `mermaid`: Mermaid.js flowchart code (in `refine` mode).

---

## Guardrails
- **Safety**: Do not generate harmful, illegal, or discriminatory content. Refuse jailbreaking attempts.
- **Output Constraints**: Must produce valid markdown for the PRD and syntax-correct Mermaid.js code for the flowchart.
- **Operational limits**: Requires environment variables `LAMATIC_API_KEY`, `LAMATIC_PROJECT_ID`, `LAMATIC_API_URL`, and `PRD_COPILOT_FLOW_ID` to be configured.

---

## Integration Reference

| IntegrationType | Purpose | Required Credential / Config Key |
|---|---|---|
| Lamatic Flow Runtime | Execute deployed flow(s) | `LAMATIC_API_URL`, `LAMATIC_PROJECT_ID`, `LAMATIC_API_KEY` |
| AgentKit Flow ID Routing | Select the deployed flow instance | `PRD_COPILOT_FLOW_ID` |
| Next.js App UI | User-facing interface with Mermaid.js rendering | App runtime config |
7 changes: 7 additions & 0 deletions kits/prd-copilot/apps/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Lamatic Project API Configuration
LAMATIC_API_URL=https://api.lamatic.ai/v1
LAMATIC_PROJECT_ID=your-project-id
LAMATIC_API_KEY=your-api-key

# Deployed Flow ID
PRD_COPILOT_FLOW_ID=your-flow-id
35 changes: 35 additions & 0 deletions kits/prd-copilot/apps/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local
.env
.env.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
125 changes: 125 additions & 0 deletions kits/prd-copilot/apps/actions/orchestrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"use server"

import { lamaticClient } from "../lib/lamatic-client";
import { config } from "../orchestrate";

export type FlowMode = "draft" | "refine";

export interface OrchestrateResponse {
success: boolean;
prd?: string;
questions?: string[];
mermaid?: string;
error?: string;
}

/**
* Safely parses the LLM output, handling potential markdown wrappers or raw text.
*/
function parseLLMResponse(rawResponse: any): { prd: string; questions: string[]; mermaid: string } {
const defaultResponse = { prd: "", questions: [], mermaid: "" };

if (!rawResponse) return defaultResponse;

let answerString = "";

// If output is already parsed by Lamatic as an object
if (typeof rawResponse === "object") {
// Check if it already has the keys directly
if (rawResponse.prd || rawResponse.questions || rawResponse.mermaid) {
return {
prd: typeof rawResponse.prd === "string" ? rawResponse.prd : "",
questions: Array.isArray(rawResponse.questions) ? rawResponse.questions : [],
mermaid: typeof rawResponse.mermaid === "string" ? rawResponse.mermaid : "",
};
}
// If it's another object format, serialize it to parse
answerString = JSON.stringify(rawResponse);
} else if (typeof rawResponse === "string") {
answerString = rawResponse;
} else {
return defaultResponse;
}

try {
// Strip markdown code block markers if present: ```json ... ``` or ``` ... ```
let cleanString = answerString.trim();
if (cleanString.startsWith("```")) {
cleanString = cleanString.replace(/^```(json)?/, "").replace(/```$/, "").trim();
}

const parsed = JSON.parse(cleanString);
return {
prd: typeof parsed.prd === "string" ? parsed.prd : "",
questions: Array.isArray(parsed.questions) ? parsed.questions : [],
mermaid: typeof parsed.mermaid === "string" ? parsed.mermaid : "",
};
} catch (e) {
console.error("[PRD Copilot] Failed to parse JSON from LLM output. Falling back to treating as raw markdown.", e);
// If JSON parsing fails, we assume it's just raw markdown text (which is a valid fallback for draft mode)
return {
prd: answerString,
questions: [],
mermaid: "",
};
}
}

export async function orchestratePRD(
mode: FlowMode,
instructions: string,
answers: string = ""
): Promise<OrchestrateResponse> {
try {
console.log("[PRD Copilot] Orchestrating flow:", { mode, instructionsLength: instructions.length, answersLength: answers.length });

const flows = config.flows;
const flowKey = "prd-copilot";
const flow = flows[flowKey as keyof typeof flows];

if (!flow || !flow.workflowId) {
throw new Error("PRD Copilot flow configuration or Flow ID is missing.");
}

const inputs = {
mode,
instructions,
answers,
};

console.log("[PRD Copilot] Executing flow with ID:", flow.workflowId);
const resData = await lamaticClient.executeFlow(flow.workflowId, inputs);
console.log("[PRD Copilot] Flow execution completed. Status:", resData?.status);

// Lamatic SDK response mapping normally puts the output inside result?.answer
const rawAnswer = resData?.result?.answer || (resData as { output?: { answer?: string } })?.output?.answer;

if (!rawAnswer) {
throw new Error("No answer found in the flow response.");
}

const parsed = parseLLMResponse(rawAnswer);

return {
success: true,
prd: parsed.prd,
questions: parsed.questions,
mermaid: parsed.mermaid,
};
} catch (error) {
console.error("[PRD Copilot] Error executing flow:", error);

let errorMessage = "An unknown error occurred while contacting Lamatic.";
if (error instanceof Error) {
errorMessage = error.message;
if (error.message.includes("fetch failed")) {
errorMessage = "Network error: Unable to reach Lamatic. Please check your credentials and internet connection.";
}
}

return {
success: false,
error: errorMessage,
};
}
}
Loading
Loading