Showcasing Clean Architecture & Modern Development Practices
// lib/db/queries.ts
export async function findSimilarClauses(
embedding: number[],
clauseType: ClauseType,
limit: number = 5
): Promise<SimilarClause[]> {
const { data, error } = await supabase
.rpc('match_clauses', {
query_embedding: embedding,
match_threshold: 0.7,
match_count: limit,
clause_type_filter: clauseType
})
if (error) throw new Error(`Query failed: ${error.message}`)
return data.map((clause) => ({
...clause,
similarity: 1 - clause.distance // Convert distance to similarity
}))
}
// lib/ai/prompts.ts
export const CONTRACT_ANALYSIS_PROMPT = `You are a contract analysis expert helping freelancers.
Analyze the following contract and identify problematic clauses.
CONTRACT TEXT:
\${contractText}
Respond with JSON in this exact format:
{
"contract_type": "NDA" | "Service Agreement" | "Employment Contract",
"overall_risk": "high" | "medium" | "low",
"risk_score": 0-100,
"flagged_clauses": [
{
"clause_text": "exact text from contract",
"clause_type": "termination" | "payment" | "liability" | "ip_rights",
"risk_level": "high" | "medium" | "low",
"explanation": "why this concerns freelancers",
"suggestion": "alternative language or negotiation point"
}
]
}
Focus on freelancer-specific risks:
- Unfair payment terms
- Excessive liability exposure
- IP rights transfers
- Restrictive non-compete clauses
Return ONLY valid JSON, no additional text.`
-- Database RLS Policy
CREATE POLICY "Users can view their own contracts"
ON contracts FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert their own contracts"
ON contracts FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Vector similarity search function
CREATE FUNCTION match_clauses(
query_embedding vector(1536),
match_threshold float,
match_count int,
clause_type_filter text
)
RETURNS TABLE (
id uuid,
clause_text text,
explanation text,
distance float
) AS $$
SELECT
id,
clause_text,
explanation,
embedding <=> query_embedding AS distance
FROM standard_clauses
WHERE clause_type = clause_type_filter
AND embedding <=> query_embedding < match_threshold
ORDER BY distance
LIMIT match_count;
$$ LANGUAGE sql;
// scripts/setup-env.js
const questions = [
{
key: 'GROQ_API_KEY',
prompt: '🔹 Groq API Key (gsk_...): ',
required: true,
validate: (val) => val.startsWith('gsk_')
}
]
function createEnvFile() {
let content = '# Clausify Environment Variables\n'
content += `# Generated: ${new Date().toISOString()}\n\n`
Object.entries(envVars).forEach(([key, value]) => {
content += `${key}=${value}\n`
})
fs.writeFileSync('.env.local', content)
console.log('✅ Environment configured successfully!')
}